From 185d3f11c2ad1a20873e1296fdb1dd5799e87cce Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Mon, 21 Sep 2026 17:37:23 -0700 Subject: [PATCH 01/11] feat(setup): consolidate Windows installers into one bootstrap with in-VS-Code mode prompt ADO #7895603. Changes - Install-EssAdk.ps1: new -InstallMode lite|standard|prompt parameter, default prompt. -SkipMakerProfile kept as a back-compat alias for -InstallMode standard. The launch section switches on the resolved modeLabel: standard runs "code chat /setup", lite/prompt open the workspace and let the ESS Maker Profile extension drive from there. Section 5c always installs the extension and writes essMaker.mode to VS Code user settings; prompt mode writes an empty string so the extension knows to ask the maker on first launch. - bootstrap.ps1: stops passing SkipMakerProfile=true, so the single Windows one-liner now defaults to the in-VS-Code mode prompt. - bootstrap-lite.ps1: reduced to a back-compat shim that pins -InstallMode lite so pre-existing lite-mode links keep working. - extension.js (v0.4.26): on first launch, if essMaker.mode is empty or "prompt", show a QuickPick (Standard recommended / Lite chat-first); persist the choice to the global setting and dispatch to the matching layout/injection flow. Dismissing the QuickPick defaults to standard. - install-telemetry.ps1: removed the "lite installer not instrumented" early return (lite is now the same installer with -InstallMode lite); added installMode dimension to every ESSMakerKit.Installer.* event. - Tests: Install-EssAdk.Tests.ps1 asserts the new -InstallMode param, the SkipMakerProfile back-compat coercion, the essMaker.mode empty-on- prompt write, the installMode dim, and the removed PS lite guard. extension.test.js asserts promptForInstallMode is declared, offers Standard + Lite, defaults to standard on dismiss, and persists the chosen mode to ConfigurationTarget.Global. - README.md (setup): updated to note the in-VS-Code prompt and keep the lite-mode link as a back-compat shortcut for scripts that must pin the choice up front. macOS scripts intentionally unchanged (per AC on ADO #7895603); the bash emitter's lite guard stays in place until a follow-up US migrates bootstrap-lite-mac.sh. Verified - pwsh setup/Install-EssAdk.Tests.ps1 -> 48/48 pass - node tools/ess-maker-profile/extension/extension.test.js -> 46/46 pass Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- setup/Install-EssAdk.Tests.ps1 | 96 ++++++++--- setup/Install-EssAdk.ps1 | 67 ++++++-- setup/README.md | 8 +- setup/bootstrap-lite.ps1 | 22 +-- setup/bootstrap.ps1 | 2 +- setup/telemetry/install-telemetry.ps1 | 17 +- .../ess-maker-profile/extension/CHANGELOG.md | 14 ++ .../ess-maker-profile/extension/extension.js | 151 +++++++++++++----- .../extension/extension.test.js | 23 +++ .../ess-maker-profile/extension/package.json | 4 +- 10 files changed, 309 insertions(+), 95 deletions(-) diff --git a/setup/Install-EssAdk.Tests.ps1 b/setup/Install-EssAdk.Tests.ps1 index 743b89c66..2068bbc24 100644 --- a/setup/Install-EssAdk.Tests.ps1 +++ b/setup/Install-EssAdk.Tests.ps1 @@ -34,12 +34,27 @@ Test 'script parses without syntax errors' { } } -Test 'script declares SkipMakerProfile parameter' { +Test 'script declares InstallMode parameter' { + if ($src -notmatch "\[string\]\s*\`$InstallMode\s*=\s*'prompt'") { + throw 'InstallMode parameter with prompt default not found' + } + if ($src -notmatch "ValidateSet\('lite',\s*'standard',\s*'prompt'\)") { + throw 'InstallMode ValidateSet(lite,standard,prompt) not found' + } +} + +Test 'script declares SkipMakerProfile parameter (back-compat)' { if ($src -notmatch '\[switch\]\s*\$SkipMakerProfile') { throw 'SkipMakerProfile switch parameter not found' } } +Test 'SkipMakerProfile forces InstallMode standard for back-compat' { + if ($src -notmatch 'if\s*\(\$SkipMakerProfile\)\s*\{\s*\$InstallMode\s*=\s*''standard''\s*\}') { + throw 'SkipMakerProfile back-compat coercion to InstallMode=standard not found' + } +} + Test 'script declares SkipLaunch parameter' { if ($src -notmatch '\[switch\]\s*\$SkipLaunch') { throw 'SkipLaunch switch parameter not found' @@ -74,9 +89,12 @@ Test 'non-git directory detection exists' { } } -Test 'extension installs in both modes (writes essMaker.mode setting)' { - if ($src -notmatch "essMaker\.mode.*\`$modeLabel") { - throw 'Mode setting write logic not found' +Test 'extension installs in every mode (writes essMaker.mode setting; prompt -> empty)' { + if ($src -notmatch "essMaker\.mode.*\`$settingsModeValue") { + throw 'Mode setting write logic (uses $settingsModeValue) not found' + } + if ($src -notmatch "if\s*\(\`$modeLabel\s+-eq\s+'prompt'\)\s*\{\s*''") { + throw "prompt mode should map to '' in essMaker.mode (extension prompts on first launch)" } } @@ -92,9 +110,12 @@ Test 'bootstrap.ps1 parses without errors' { if ($errors.Count -gt 0) { throw "Parse errors: $($errors[0].Message)" } } -Test 'bootstrap.ps1 passes SkipMakerProfile = $true' { - if ($bootstrapSrc -notmatch 'SkipMakerProfile\s*=\s*\$true') { - throw 'SkipMakerProfile not set to $true in standard bootstrap' +Test 'bootstrap.ps1 does not pin SkipMakerProfile (consolidated installer prompts in VS Code)' { + if ($bootstrapSrc -match 'SkipMakerProfile\s*=\s*\$true') { + throw 'bootstrap.ps1 should not set SkipMakerProfile = $true after installer consolidation (ADO #7895603)' + } + if ($bootstrapSrc -match "InstallMode\s*=\s*'(lite|standard)'") { + throw "bootstrap.ps1 should not pin InstallMode; it should let the installer default to 'prompt' so VS Code asks the maker." } } @@ -109,11 +130,13 @@ Test 'bootstrap-lite.ps1 parses without errors' { if ($errors.Count -gt 0) { throw "Parse errors: $($errors[0].Message)" } } -Test 'bootstrap-lite.ps1 does NOT pass SkipMakerProfile as an argument' { - # It may mention SkipMakerProfile in a comment, but the actual args hash should not include it +Test 'bootstrap-lite.ps1 passes -InstallMode lite (back-compat shim)' { if ($liteSrc -match 'SkipMakerProfile\s*=\s*\$true') { throw 'bootstrap-lite.ps1 should not set SkipMakerProfile = $true' } + if ($liteSrc -notmatch "InstallMode\s*=\s*'lite'") { + throw "bootstrap-lite.ps1 should pin InstallMode = 'lite' so old links keep landing in lite mode" + } } # --------------------------------------------------------------------------- @@ -169,9 +192,16 @@ Test 'defines no-op telemetry stubs when the emitter is absent (fail-open)' { if ($src -notmatch 'function Initialize-EssInstallTelemetry') { throw 'no-op stub fallback not found' } } -Test 'derives installer mode (flightcheck | adk | lite)' { +Test 'derives installer mode (flightcheck | adk) - lite consolidated into adk' { if ($src -notmatch "FlightCheckOnly.*'flightcheck'") { throw 'flightcheck mode not derived' } - if ($src -notmatch "SkipMakerProfile.*'adk'") { throw 'adk mode not derived' } + if ($src -notmatch "\}\s*else\s*\{\s*'adk'\s*\}") { throw "post-consolidation installer identity should collapse to 'adk' for non-flightcheck installs" } + if ($src -match "SkipMakerProfile.*'adk'.*else.*'lite'") { throw 'lite installer identity should no longer be derived here (installMode dim carries the distinction)' } +} + +Test 'passes -InstallMode to Initialize-EssInstallTelemetry' { + if ($src -notmatch 'Initialize-EssInstallTelemetry\s+-Installer\s+\$essInstaller\s+-InstallMode\s+\$modeLabel') { + throw '-InstallMode $modeLabel must be threaded into telemetry init' + } } Test 'Write-Step is hooked to emit a per-step telemetry event' { @@ -239,21 +269,47 @@ Test 'envelope time is culture-invariant ISO-8601 (non-colon-separator locales)' [System.Threading.Thread]::CurrentThread.CurrentCulture = $orig } } -Test 'lite installer is not instrumented (no telemetry ready, no events)' { +Test 'installer telemetry emits an installMode dimension' { + Initialize-EssInstallTelemetry -Installer 'adk' -InstallMode 'lite' + try { + $data = Get-EssTelCommonData + if (-not $data.ContainsKey('installMode')) { throw 'installMode dimension not emitted' } + if ($data.installMode -ne 'lite') { throw "installMode wrong: $($data.installMode)" } + } finally { $script:EssTel.Ready = $false; $script:EssTel.Completed = $false } +} + +Test 'installer telemetry defaults installMode to prompt when unspecified' { + Initialize-EssInstallTelemetry -Installer 'adk' + try { + $data = Get-EssTelCommonData + if ($data.installMode -ne 'prompt') { throw "expected default prompt, got: $($data.installMode)" } + } finally { $script:EssTel.Ready = $false; $script:EssTel.Completed = $false } +} + +Test 'lite installer identity is instrumented (post-consolidation)' { + # Regression: pre-consolidation the emitter guarded out Installer='lite' + # because the lite installer was slated for removal. With bootstrap-lite.ps1 + # now a compat shim into the unified installer that passes -InstallMode lite, + # the guard would drop all lite-shim events. It must be gone. $old = $env:ESS_ADK_TELEMETRY try { - $env:ESS_ADK_TELEMETRY = '' # ensure telemetry is otherwise enabled - Initialize-EssInstallTelemetry -Installer 'lite' - if ($script:EssTel.Ready) { throw 'lite installer should not be telemetry-ready' } - } finally { $env:ESS_ADK_TELEMETRY = $old } + $env:ESS_ADK_TELEMETRY = '' + Initialize-EssInstallTelemetry -Installer 'lite' -InstallMode 'lite' + if (-not $script:EssTel.Ready) { throw 'lite installer should be telemetry-ready after consolidation' } + } finally { $env:ESS_ADK_TELEMETRY = $old; $script:EssTel.Ready = $false; $script:EssTel.Completed = $false } } -Test 'PowerShell emitter guards out the lite installer' { +Test 'PowerShell emitter no longer guards out the lite installer' { $emitterSrc = Get-Content $psEmitter -Raw - if ($emitterSrc -notmatch "Installer\s+-eq\s+'lite'") { throw 'lite guard missing in PS emitter' } + if ($emitterSrc -match "if\s*\(\`$Installer\s+-eq\s+'lite'\)\s*\{\s*\`$script:EssTel\.Ready\s*=\s*\`$false") { + throw 'PS emitter still guards out lite installer; guard should be removed after consolidation (ADO #7895603)' + } } -Test 'bash emitter guards out the lite installer' { +Test 'bash emitter still guards out the lite installer (macOS scope unchanged in this iteration)' { + # macOS consolidation is out of scope for ADO #7895603. Until a follow-up + # US addresses macOS, bootstrap-lite-mac.sh remains a separate installer + # tagged as ess_tel_installer=lite and the bash emitter still gates it out. $shSrc = Get-Content $shEmitter -Raw - if ($shSrc -notmatch 'ESS_TEL_INSTALLER.*==.*"lite"') { throw 'lite guard missing in bash emitter' } + if ($shSrc -notmatch 'ESS_TEL_INSTALLER.*==.*"lite"') { throw 'lite guard missing in bash emitter (macOS scope not yet migrated)' } } # --- macOS installer + all bootstraps wiring ------------------------------- diff --git a/setup/Install-EssAdk.ps1 b/setup/Install-EssAdk.ps1 index 99b5ff870..a0c2eb253 100644 --- a/setup/Install-EssAdk.ps1 +++ b/setup/Install-EssAdk.ps1 @@ -53,12 +53,19 @@ Prompts for your Dataverse environment URL and creates a minimal .local/config.json so FlightCheck can authenticate without running /setup. +.PARAMETER InstallMode + Selects the VS Code experience: 'lite' (chat-first, hidden developer + chrome), 'standard' (default VS Code layout with /setup injection), or + 'prompt' (default: let the ESS Maker Profile extension ask the maker + inside VS Code on first launch, default to standard if dismissed). The + ESS Maker Profile extension is installed in every mode; only the layout + and /setup delivery differ. Explicit values are respected without a + prompt; 'prompt' is the recommended default for new customers. + .PARAMETER SkipMakerProfile - Skip installing the bundled "ESS Maker Profile" VS Code extension. The - profile hides developer chrome (file tree, tabs, status bar, etc.) and - drops the user into a chat-first surface tailored to the HR/IT admin - persona. Use this switch to keep the stock VS Code layout - typically - only relevant for developers iterating on the kit itself. + Back-compat switch. Equivalent to -InstallMode standard. Retained so + existing bootstrap.ps1 invocations and CI scripts keep working; new + callers should use -InstallMode instead. .EXAMPLE # Default invocation. May fail on stock Windows due to PowerShell @@ -84,9 +91,22 @@ param( [switch] $SkipLaunch, [switch] $UseDsc, [switch] $FlightCheckOnly, + [ValidateSet('lite', 'standard', 'prompt')] + [string] $InstallMode = 'prompt', [switch] $SkipMakerProfile ) +# Back-compat: -SkipMakerProfile forces standard mode even when +# -InstallMode is passed. This preserves the old behaviour where the +# switch was the only way to say "no chat-first layout". +if ($SkipMakerProfile) { $InstallMode = 'standard' } + +# Canonical mode label used throughout this script for both telemetry and +# the VS Code settings write. Kept in $modeLabel so all downstream +# references (5c extension install, launch, telemetry) share one source +# of truth. +$modeLabel = $InstallMode + $ErrorActionPreference = 'Stop' function Write-Step { param([string]$m) Write-Host "`n==> $m" -ForegroundColor Cyan; try { Write-EssInstallStep -Step (Get-EssStepKey $m) } catch {} } @@ -413,8 +433,8 @@ if (-not $essTelLoaded) { function Complete-EssInstallTelemetry { param($Outcome, $ErrorRecord) } } -$essInstaller = if ($FlightCheckOnly) { 'flightcheck' } elseif ($SkipMakerProfile) { 'adk' } else { 'lite' } -Initialize-EssInstallTelemetry -Installer $essInstaller +$essInstaller = if ($FlightCheckOnly) { 'flightcheck' } else { 'adk' } +Initialize-EssInstallTelemetry -Installer $essInstaller -InstallMode $modeLabel try { @@ -964,10 +984,12 @@ if (-not $FlightCheckOnly) { # Skipped in FlightCheckOnly mode (no VS Code launch) and when the user # passes -SkipExtensions (IT-locked-down boxes that block VSIX installs). if (-not $FlightCheckOnly -and -not $SkipExtensions) { - # Install the ESS Maker Profile extension in both modes. In lite mode it - # applies the chat-first layout; in standard mode it only handles /setup - # injection after the welcome wizard closes (no visual changes). - $modeLabel = if ($SkipMakerProfile) { 'standard' } else { 'lite' } + # Install the ESS Maker Profile extension in every mode. In lite mode + # it applies the chat-first layout; in standard mode it only handles + # /setup injection after the welcome wizard closes (no visual + # changes); in prompt mode it asks the maker inside VS Code on first + # launch and applies whichever mode they pick. $modeLabel is set in + # the param block above from -InstallMode / -SkipMakerProfile. Write-Step "Installing ESS Maker Profile ($modeLabel mode)" $code = Resolve-CodeCommand @@ -1022,12 +1044,15 @@ if (-not $FlightCheckOnly -and -not $SkipExtensions) { } # Write the mode setting so the extension knows whether to apply - # the lite layout or inject /setup (standard mode). + # the lite layout, inject /setup (standard mode), or prompt the + # maker inside VS Code on first launch (prompt mode -> empty + # string, which the extension treats as "not decided"). # Uses string manipulation to preserve JSONC comments in settings.json. $settingsDir = Join-Path $env:APPDATA 'Code\User' if (-not (Test-Path $settingsDir)) { New-Item -ItemType Directory -Path $settingsDir -Force | Out-Null } $settingsFile = Join-Path $settingsDir 'settings.json' - $modeEntry = "`"essMaker.mode`": `"$modeLabel`"" + $settingsModeValue = if ($modeLabel -eq 'prompt') { '' } else { $modeLabel } + $modeEntry = "`"essMaker.mode`": `"$settingsModeValue`"" if (Test-Path $settingsFile) { $raw = Get-Content $settingsFile -Raw if ($raw -match '"essMaker\.mode"\s*:') { @@ -1408,9 +1433,13 @@ if (-not $SkipLaunch) { # handles layout + /setup injection after the welcome wizard closes. # - Standard mode: use `code chat '/setup'` which opens Copilot Chat in # the sidebar panel on the right (the standard chat experience). + # - Prompt mode: open the workspace. The ESS Maker Profile extension + # asks the maker which experience they want on first launch, then + # applies the chosen mode's flow (layout+injection for lite, no + # layout change + chat open for standard). Push-Location $workspace try { - if ($SkipMakerProfile) { + if ($modeLabel -eq 'standard') { # Standard mode - use code chat to open /setup in sidebar panel Write-Step 'Opening workspace in VS Code and requesting /setup in Copilot Chat' $chatOutput = Invoke-Native { & $codePath chat '/setup' } @@ -1427,13 +1456,21 @@ if (-not $SkipLaunch) { Write-Host "If VS Code prompts you to trust the workspace or sign in to GitHub/Copilot, accept those prompts and /setup will run." -ForegroundColor Yellow Write-Host "If /setup does not start after trust/sign-in, open Copilot Chat manually and run /setup." -ForegroundColor Yellow } - } else { + } elseif ($modeLabel -eq 'lite') { # Lite mode - extension handles /setup after welcome wizard Write-Step 'Opening workspace in VS Code' Start-Process -FilePath $codePath -ArgumentList @('.') | Out-Null Write-Ok "Launched VS Code at $workspace" Write-Host "The ESS Maker Profile will run /setup in Copilot Chat after the welcome screen closes." -ForegroundColor Yellow Write-Host "If VS Code prompts you to trust the workspace, accept the prompt." -ForegroundColor Yellow + } else { + # Prompt mode - extension will ask which experience to apply + # on first launch, then run the appropriate flow. + Write-Step 'Opening workspace in VS Code' + Start-Process -FilePath $codePath -ArgumentList @('.') | Out-Null + Write-Ok "Launched VS Code at $workspace" + Write-Host "VS Code will ask you to pick a chat-first (lite) or standard developer experience on first launch." -ForegroundColor Yellow + Write-Host "If VS Code prompts you to trust the workspace, accept the prompt." -ForegroundColor Yellow } } finally { Pop-Location } } else { diff --git a/setup/README.md b/setup/README.md index d70990656..b24718520 100644 --- a/setup/README.md +++ b/setup/README.md @@ -14,13 +14,15 @@ iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-mac.sh)" ``` -Once complete, VS Code opens at `solutions/ess-maker-skills/` and `/setup` is automatically requested in Copilot Chat. You'll be prompted to trust the workspace and sign in to GitHub/Copilot — accept these prompts and `/setup` will connect the workspace to an existing editable DA Dev agent. +Once complete, VS Code opens at `solutions/ess-maker-skills/` and — on the first launch — you'll be asked to pick **Standard** (default developer view with Copilot Chat in the side panel) or **Lite** (chat-first, big-button layout). Standard is the recommended default and will be applied if you dismiss the prompt. `/setup` is then automatically requested in Copilot Chat. You'll be prompted to trust the workspace and sign in to GitHub/Copilot — accept these prompts and `/setup` will connect the workspace to an existing editable DA Dev agent. > **GitHub Copilot subscription is required** for the in-editor maker experience. This script installs the toolchain and extension scaffolding; it does not grant the Copilot entitlement. ## Lite Mode (Chat-First Layout) -For users who prefer a simplified, chat-first experience that hides developer chrome (file tree, tabs, status bar) and shows a "Quick Actions" button rail: +The one-shot Windows installer (`bootstrap.ps1`) will ask you to choose between Standard and Lite on first VS Code launch, so lite mode is a one-click choice from the main installer — no separate command needed. + +For scripts and docs that need to pin the choice up front (bypassing the in-VS-Code prompt), a lite-mode shortcut is retained: **Windows** (PowerShell): @@ -34,7 +36,7 @@ iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-lite-mac.sh)" ``` -This installs everything the standard installer does, plus the **ESS Maker Profile** extension which provides: +Lite mode is the same install as standard plus the **ESS Maker Profile** extension applying: - A chat-only layout with all developer surfaces hidden - Big-button "Quick Actions" rail for common tasks (Connect, Customize landing page, Create, Scan, FlightCheck, Push) - A built-in tutorial explaining each button diff --git a/setup/bootstrap-lite.ps1 b/setup/bootstrap-lite.ps1 index 6e52777ae..9ac963a16 100644 --- a/setup/bootstrap-lite.ps1 +++ b/setup/bootstrap-lite.ps1 @@ -1,19 +1,21 @@ <# .SYNOPSIS - One-liner bootstrap for the ESS Maker Kit in Lite Mode. + Back-compat one-liner bootstrap that pins the installer to Lite Mode. .DESCRIPTION - Downloads the installer script into a temp folder and runs it with the - ESS Maker Profile extension enabled. This gives users a chat-first, - big-button experience that hides developer chrome (file tree, tabs, - status bar, etc.) and surfaces a "Quick Actions" button rail. + Downloads the installer and runs it with -InstallMode lite. Kept so + existing links (docs, blog posts, share sheets) that point at + bootstrap-lite.ps1 keep working after the standard + lite installers + were merged into a single bootstrap.ps1. - Designed to be invoked from a single command: + New customers should use bootstrap.ps1, which prompts inside VS Code + on first launch to pick the experience. This shim is documented as + a redirect only. iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-lite.ps1) All real work happens in Install-EssAdk.ps1; this file just gets the bits - onto the customer's machine and ensures the Maker Profile is installed. + onto the customer's machine and pins the mode to lite. .PARAMETER InstallRoot Forwarded to Install-EssAdk.ps1. See that script for details. @@ -79,8 +81,10 @@ $installer = Join-Path $tempDir 'Install-EssAdk.ps1' $scriptContent = [System.IO.File]::ReadAllText($installer, [System.Text.Encoding]::UTF8) $scriptBlock = [ScriptBlock]::Create($scriptContent) -# Lite mode: do NOT pass -SkipMakerProfile so the chat-first profile installs. -$installerArgs = @{ Branch = $Branch } +# Lite mode: pass -InstallMode lite so the ESS Maker Profile applies the +# chat-first layout without asking the maker. Kept as a compat shim while +# the single bootstrap.ps1 becomes the recommended entry point. +$installerArgs = @{ Branch = $Branch; InstallMode = 'lite' } if ($InstallRoot) { $installerArgs.InstallRoot = $InstallRoot } & $scriptBlock @installerArgs diff --git a/setup/bootstrap.ps1 b/setup/bootstrap.ps1 index 7763323ad..55b30136a 100644 --- a/setup/bootstrap.ps1 +++ b/setup/bootstrap.ps1 @@ -75,7 +75,7 @@ $installer = Join-Path $tempDir 'Install-EssAdk.ps1' $scriptContent = [System.IO.File]::ReadAllText($installer, [System.Text.Encoding]::UTF8) $scriptBlock = [ScriptBlock]::Create($scriptContent) -$installerArgs = @{ Branch = $Branch; SkipMakerProfile = $true } +$installerArgs = @{ Branch = $Branch } if ($InstallRoot) { $installerArgs.InstallRoot = $InstallRoot } & $scriptBlock @installerArgs diff --git a/setup/telemetry/install-telemetry.ps1 b/setup/telemetry/install-telemetry.ps1 index fad04cace..73f6bb574 100644 --- a/setup/telemetry/install-telemetry.ps1 +++ b/setup/telemetry/install-telemetry.ps1 @@ -48,6 +48,7 @@ $script:EssTelSchemaVersion = '1.0' $script:EssTel = @{ Ready = $false Installer = 'adk' + InstallMode = 'prompt' Env = 'prod' IKey = '' InstanceId = '' @@ -208,6 +209,7 @@ function Get-EssTelCommonData { schemaVersion = $script:EssTelSchemaVersion env = $script:EssTel.Env installer = $script:EssTel.Installer + installMode = $script:EssTel.InstallMode invocationSource = 'installer' platform = $script:EssTel.Platform os = $script:EssTel.OsVersion @@ -220,17 +222,21 @@ function Get-EssTelCommonData { function Initialize-EssInstallTelemetry { <# .SYNOPSIS Begin installer telemetry: notice, identity, and the start event. - .PARAMETER Installer One of adk | lite | flightcheck. + .PARAMETER Installer One of adk | lite | flightcheck. 'lite' is retained + for the back-compat bootstrap-lite.ps1 shim; new callers should pass + 'adk' with -InstallMode lite instead. + .PARAMETER InstallMode The VS Code experience the maker will land in + after the installer completes: lite | standard | prompt. 'prompt' + means the ESS Maker Profile extension will ask on first launch. #> param( [ValidateSet('adk', 'lite', 'flightcheck')] - [string]$Installer = 'adk' + [string]$Installer = 'adk', + [ValidateSet('lite', 'standard', 'prompt')] + [string]$InstallMode = 'prompt' ) try { if (-not (Test-EssTelemetryEnabled)) { $script:EssTel.Ready = $false; return } - # The Lite-mode installer is being merged into the standard ADK installer - # (mode will become an onboarding prompt), so it is no longer instrumented. - if ($Installer -eq 'lite') { $script:EssTel.Ready = $false; return } Initialize-EssTelConfig $envName = "$env:ESS_ADK_ARIA_ENV".Trim().ToLowerInvariant() @@ -241,6 +247,7 @@ function Initialize-EssInstallTelemetry { $adkVer = "$env:ESS_ADK_VERSION".Trim(); if (-not $adkVer) { $adkVer = 'unknown' } $script:EssTel.Installer = $Installer + $script:EssTel.InstallMode = $InstallMode $script:EssTel.Env = $envName $script:EssTel.IKey = $script:EssTelIKeys[$envName] $script:EssTel.InstanceId = $inst.Id diff --git a/tools/ess-maker-profile/extension/CHANGELOG.md b/tools/ess-maker-profile/extension/CHANGELOG.md index 1fe4343b8..a71d0be48 100644 --- a/tools/ess-maker-profile/extension/CHANGELOG.md +++ b/tools/ess-maker-profile/extension/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.4.26 (POC) + +- **First-launch mode prompt.** After the consolidated Windows installer + (see ADO #7895603 — one `bootstrap.ps1` for both experiences), the + extension asks the maker on first VS Code launch which experience they + want: **Standard (recommended)** — default developer view with GitHub + Copilot Chat in the side panel and automatic `/setup` — or **Lite + (chat-first)** — hidden developer chrome and the Quick Actions rail. + The choice is persisted to the `essMaker.mode` global setting so + subsequent launches skip the prompt; dismissing the QuickPick defaults + to Standard. Legacy invocations that pin `essMaker.mode` to `lite` or + `standard` (including the back-compat `bootstrap-lite.ps1` shim) + bypass the prompt. + ## 0.4.25 (POC) - **Customize landing page** is available in Quick Actions. The setup-gated action opens a guided Copilot chat for branding, quick links, starter prompts, and insight cards. diff --git a/tools/ess-maker-profile/extension/extension.js b/tools/ess-maker-profile/extension/extension.js index a8a244db7..ed34eed05 100644 --- a/tools/ess-maker-profile/extension/extension.js +++ b/tools/ess-maker-profile/extension/extension.js @@ -1425,6 +1425,110 @@ async function maybePromptReinstall(repoRoot) { } } +// --- First-install dispatch (ADO #7895603 consolidated installer) --------- +// When the consolidated installer leaves essMaker.mode unset ("" or +// "prompt"), we prompt the maker on their first VS Code launch to pick +// lite (chat-first) vs standard (developer view) and persist the choice +// to the essMaker.mode global setting so subsequent launches skip the +// prompt. This exists so the consolidated Windows installer doesn't need +// to interrupt the CLI with a mode question — the choice surfaces in +// VS Code where the maker can preview the two options as they read them. +async function promptForInstallMode() { + const picks = [ + { + label: 'Standard (recommended)', + description: 'Default VS Code layout with GitHub Copilot Chat in the side panel', + detail: 'Best if you plan to inspect or edit files directly. /setup runs automatically.', + mode: 'standard', + }, + { + label: 'Lite (chat-first)', + description: 'Hides file tree, tabs, and status bar; big-button Quick Actions rail', + detail: 'Best if you mostly work in chat and want a focused HR/IT admin surface.', + mode: 'lite', + }, + ]; + try { + const pick = await vscode.window.showQuickPick(picks, { + placeHolder: 'Choose your ESS Maker experience (you can change this later from the Command Palette)', + ignoreFocusOut: true, + matchOnDescription: true, + matchOnDetail: true, + }); + return pick ? pick.mode : 'standard'; + } catch (err) { + _log(`promptForInstallMode: error ${err && err.message}`); + return 'standard'; + } +} + +async function firstInstallDispatch(context, installerMode) { + let effectiveMode = installerMode; + if (!effectiveMode || effectiveMode === 'prompt') { + effectiveMode = await promptForInstallMode(); + try { + await vscode.workspace.getConfiguration().update( + 'essMaker.mode', + effectiveMode, + vscode.ConfigurationTarget.Global, + ); + } catch (err) { + _log(`firstInstallDispatch: failed to persist essMaker.mode: ${err && err.message}`); + } + } + const isStandardMode = effectiveMode === 'standard'; + _log(`firstInstallDispatch: effectiveMode=${effectiveMode}, isStandardMode=${isStandardMode}`); + context.globalState.update(LITE_MODE_KEY, !isStandardMode); + + // Check if the user already has a config file (returning user who + // re-ran the installer). Skip /setup if already configured. + let alreadyConfigured = false; + try { + const met = await checkPrerequisites(); + alreadyConfigured = met.has('setup'); + } catch (err) { + _log(`firstInstallDispatch: checkPrerequisites error: ${err && err.message}`); + } + _log(`firstInstallDispatch: alreadyConfigured=${alreadyConfigured}`); + + if (isStandardMode) { + // Standard mode: no layout changes. In prompt mode the installer + // could not eagerly run `code chat '/setup'` (mode was unknown at + // launch time), so surface /setup here for the not-yet-configured + // path — otherwise a maker who picks Standard here would land in + // an empty chat with no cue what to do next. + context.globalState.update(APPLIED_KEY, true); + if (!alreadyConfigured) { + _log('firstInstallDispatch: standard mode, running /setup via injectSetup'); + waitForWelcomeWizard() + .then(() => new Promise(r => setTimeout(r, 3000))) + .then(() => injectSetup()) + .catch((err) => _log(`firstInstallDispatch: standard injectSetup error: ${err && err.message}`)); + } else { + _log('firstInstallDispatch: standard mode, already configured — no action'); + } + return; + } + + // Lite mode: apply layout. + applyChatOnlyLayout({ silent: false }) + .then(() => context.globalState.update(APPLIED_KEY, true)) + .catch(() => {}); + if (alreadyConfigured) { + _log('firstInstallDispatch: skipping /setup (already configured), opening chat'); + setTimeout(() => tryRun('workbench.action.chat.open').catch(() => {}), 3000); + } else { + waitForWelcomeWizard() + .then(() => { _log('firstInstallDispatch: wizard done (lite), waiting 3s...'); return new Promise(r => setTimeout(r, 3000)); }) + .then(() => { _log('firstInstallDispatch: calling injectSetup (lite)'); return injectSetup(); }) + .then(() => _log('firstInstallDispatch: injectSetup completed (lite)')) + .catch((err) => { + _log(`firstInstallDispatch: ERROR in lite wizard chain: ${err && err.message}`); + console.warn('[ess-maker] Welcome wizard wait timed out, skipping auto /setup'); + }); + } +} + function activate(context) { _extensionContext = context; _log(`activate: ENTRY. workspaceFolders=${JSON.stringify(vscode.workspace.workspaceFolders?.map(f => f.uri.fsPath))}`); @@ -1475,6 +1579,9 @@ function activate(context) { // written by the installer. // Lite mode: applies chat-only layout; user clicks Setup to run /setup. // Standard mode: injects /setup into Copilot Chat automatically. + // Empty ("") / "prompt": consolidated installer (ADO #7895603) left + // the choice to us — show a QuickPick, default to standard, then + // route into the chosen branch. // - Subsequent lite activations: silently re-apply layout. const alreadyApplied = context.globalState.get(APPLIED_KEY, false); const userWantsLite = context.globalState.get(LITE_MODE_KEY, true); // default to lite @@ -1484,46 +1591,10 @@ function activate(context) { if (vscode.workspace.workspaceFolders?.length) { if (!alreadyApplied) { - // First install. Check installer-provided mode setting. - const isStandardMode = installerMode === 'standard'; - _log(`activate: first install, isStandardMode=${isStandardMode}`); - context.globalState.update(LITE_MODE_KEY, !isStandardMode); - - // Check if the user already has a config file (returning user - // who re-ran the installer). Skip /setup if already configured. - checkPrerequisites().then(met => { - const alreadyConfigured = met.has('setup'); - _log(`activate: alreadyConfigured=${alreadyConfigured}`); - - if (isStandardMode) { - // Standard mode: no layout changes. The installer handles - // /setup injection via `code chat` which opens in the - // sidebar panel. Nothing to do here. - context.globalState.update(APPLIED_KEY, true); - _log('activate: standard mode — installer handles /setup via code chat'); - } else { - // Lite mode: apply layout. - applyChatOnlyLayout({ silent: false }) - .then(() => context.globalState.update(APPLIED_KEY, true)) - .catch(() => {}); - if (alreadyConfigured) { - _log('activate: skipping /setup (already configured), opening chat'); - // Returning user in lite mode — just open the chat panel - // so they can start working right away. - setTimeout(() => tryRun('workbench.action.chat.open').catch(() => {}), 3000); - } else { - // Wait for welcome wizard to finish, then inject /setup. - waitForWelcomeWizard() - .then(() => { _log('activate: wizard done (lite), waiting 3s...'); return new Promise(r => setTimeout(r, 3000)); }) - .then(() => { _log('activate: calling injectSetup (lite)'); return injectSetup(); }) - .then(() => _log('activate: injectSetup completed (lite)')) - .catch((err) => { - _log(`activate: ERROR in lite wizard chain: ${err && err.message}`); - console.warn('[ess-maker] Welcome wizard wait timed out, skipping auto /setup'); - }); - } - } - }).catch(err => _log(`activate: checkPrerequisites error: ${err && err.message}`)); + // First install. Resolve mode (prompting the maker if the + // consolidated installer didn't pin it), then dispatch. + firstInstallDispatch(context, installerMode) + .catch(err => _log(`activate: firstInstallDispatch error: ${err && err.message}`)); } else if (userWantsLite) { // Subsequent lite mode launch: silently re-apply layout. setTimeout(() => { applyChatOnlyLayout({ silent: true }).catch(() => {}); }, 1500); diff --git a/tools/ess-maker-profile/extension/extension.test.js b/tools/ess-maker-profile/extension/extension.test.js index 858e93a13..786dbb369 100644 --- a/tools/ess-maker-profile/extension/extension.test.js +++ b/tools/ess-maker-profile/extension/extension.test.js @@ -200,6 +200,29 @@ test('exposes the essMaker.autoUpdateCheck opt-out setting', () => { assert.strictEqual(prop.default, true); }); +console.log('\nfirst-install mode prompt (ADO #7895603):'); + +test('extension source declares promptForInstallMode helper', () => { + assert.ok(/async function promptForInstallMode\s*\(/.test(src), 'promptForInstallMode not declared'); +}); + +test('mode prompt offers Standard and Lite choices', () => { + assert.ok(/Standard \(recommended\)/.test(src), 'Standard option label missing'); + assert.ok(/Lite \(chat-first\)/.test(src), 'Lite option label missing'); +}); + +test('mode prompt defaults to standard when the maker dismisses the QuickPick', () => { + assert.ok(/return pick \? pick\.mode : 'standard'/.test(src), 'Dismiss-defaults-to-standard fallback missing'); +}); + +test('firstInstallDispatch prompts when installerMode is empty or "prompt"', () => { + assert.ok(/if\s*\(!effectiveMode \|\| effectiveMode === 'prompt'\)/.test(src), 'promptForInstallMode should be invoked when installer setting is unset'); +}); + +test('firstInstallDispatch persists the chosen mode to global settings', () => { + assert.ok(/'essMaker\.mode',[\s\S]*?ConfigurationTarget\.Global/.test(src), 'chosen mode should be persisted with ConfigurationTarget.Global'); +}); + console.log('\nauto-update: parseLsRemoteSha:'); test('extracts sha from a ls-remote line', () => { diff --git a/tools/ess-maker-profile/extension/package.json b/tools/ess-maker-profile/extension/package.json index ee7ef39ca..3b7f2aba9 100644 --- a/tools/ess-maker-profile/extension/package.json +++ b/tools/ess-maker-profile/extension/package.json @@ -2,7 +2,7 @@ "name": "ess-maker-profile", "displayName": "ESS Maker", "description": "Chat-only, big-button experience for customizing your Employee Self-Service agent. POC.", - "version": "0.4.25", + "version": "0.4.26", "publisher": "microsoft-ess", "private": true, "engines": { @@ -94,7 +94,7 @@ "type": "string", "enum": ["lite", "standard", ""], "default": "", - "description": "Install mode set by the ESS installer. 'lite' applies chat-first layout; 'standard' uses default VS Code layout with /setup injection only." + "description": "Install mode set by the ESS installer. 'lite' applies the chat-first layout; 'standard' uses the default VS Code layout with /setup injection; empty string means the consolidated installer left the choice unset and the extension will ask on first launch." }, "essMaker.autoUpdateCheck": { "type": "boolean", From 7c5163c1e481c93dbcaa62d4fd140179f771cd36 Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Wed, 23 Sep 2026 13:20:45 -0700 Subject: [PATCH 02/11] Rename installer modes: lite -> maker (default), standard -> developer User research surfaced that `Lite` undersold the mode (it's a full, streamlined chat-first UX, not a cut-down one) and `Standard` was misleading (it already modifies UX by opening Copilot Chat and running /setup on first launch). Rename to Maker (default) / Developer across the installer stack while preserving full back-compat for the legacy lite/standard values written by previously-installed users and pinned CI scripts. Windows: - Install-EssAdk.ps1: -InstallMode ValidateSet accepts maker|developer|prompt plus legacy lite|standard; -SkipMakerProfile coerces to developer (was standard); dismissal-fallback stays prompt. - bootstrap-lite.ps1: now pins InstallMode=maker (back-compat URL). - bootstrap-dev.ps1: NEW - Developer-mode shortcut. - telemetry/install-telemetry.ps1: ValidateSet extended. - Install-EssAdk.Tests.ps1: updated to 58 tests, all green. macOS: - install-ess-adk.sh: INSTALL_MODE env var (maker|developer|prompt + legacy lite|standard) with SKIP_MAKER_PROFILE=true back-compat; MODE_LABEL / settings-write / launch branches rewritten; installer identity collapsed to flightcheck|adk with ESS_TEL_INSTALLER_OVERRIDE escape hatch for the still-legacy bootstrap-lite-mac.sh emitter guard. - bootstrap-lite-mac.sh: pins INSTALL_MODE=maker and sets ESS_TEL_INSTALLER_OVERRIDE=lite so the bash telemetry guard still drops these events until macOS consolidation ships. - bootstrap-dev-mac.sh: NEW - Developer-mode shortcut. - telemetry/install-telemetry.sh: ess_tel_init takes an install mode arg and emits it as the new installMode dimension; the ESS_TEL_INSTALLER=='lite' guard is retained. Extension (0.4.26 -> 0.4.27): - extension.js: rename isLiteMode -> isMakerLayout; button IDs btn-lite/btn-standard -> btn-maker/btn-developer; QuickPick shows Maker (recommended) / Developer with dismissal default = maker; normalizeInstallerMode() maps legacy lite->maker / standard->developer at the top of firstInstallDispatch so upgraded users don't get re-prompted; LITE_MODE_KEY = 'essMaker.liteMode.v1' on-disk name is preserved to protect existing users' persisted layout. - extension.test.js: 48 tests, all green. - package.json: essMaker.mode enum extended to include maker, developer, lite, standard, ''; command title `essMaker.restoreStandardLayout` -> `Restore Developer Layout` (command ID preserved for keybinding compat). - CHANGELOG.md: 0.4.27 entry. Docs: - setup/README.md: Maker Mode / Developer Mode section, new bootstrap-dev.ps1 and bootstrap-dev-mac.sh commands, updated file-table and testing snippets. Refs: #296 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- setup/Install-EssAdk.Tests.ps1 | 122 +++++++++++++--- setup/Install-EssAdk.ps1 | 59 ++++---- setup/README.md | 48 +++++-- setup/bootstrap-dev-mac.sh | 61 ++++++++ setup/bootstrap-dev.ps1 | 92 ++++++++++++ setup/bootstrap-lite-mac.sh | 16 ++- setup/bootstrap-lite.ps1 | 20 +-- setup/install-ess-adk.sh | 91 +++++++++--- setup/telemetry/install-telemetry.ps1 | 9 +- setup/telemetry/install-telemetry.sh | 12 +- .../ess-maker-profile/extension/CHANGELOG.md | 30 +++- .../ess-maker-profile/extension/extension.js | 132 ++++++++++-------- .../extension/extension.test.js | 28 +++- .../ess-maker-profile/extension/package.json | 8 +- 14 files changed, 558 insertions(+), 170 deletions(-) create mode 100644 setup/bootstrap-dev-mac.sh create mode 100644 setup/bootstrap-dev.ps1 diff --git a/setup/Install-EssAdk.Tests.ps1 b/setup/Install-EssAdk.Tests.ps1 index 2068bbc24..78d8f0b35 100644 --- a/setup/Install-EssAdk.Tests.ps1 +++ b/setup/Install-EssAdk.Tests.ps1 @@ -38,8 +38,14 @@ Test 'script declares InstallMode parameter' { if ($src -notmatch "\[string\]\s*\`$InstallMode\s*=\s*'prompt'") { throw 'InstallMode parameter with prompt default not found' } - if ($src -notmatch "ValidateSet\('lite',\s*'standard',\s*'prompt'\)") { - throw 'InstallMode ValidateSet(lite,standard,prompt) not found' + if ($src -notmatch "ValidateSet\('maker',\s*'developer',\s*'prompt'") { + throw "InstallMode ValidateSet should start with the canonical 'maker','developer','prompt' triple" + } + # Legacy aliases 'lite'/'standard' remain in the ValidateSet so old + # bootstrap-lite invocations and any pinned CI script don't blow up + # under the rename. + if ($src -notmatch "ValidateSet\('maker',\s*'developer',\s*'prompt',\s*'lite',\s*'standard'\)") { + throw "InstallMode ValidateSet should also accept legacy 'lite','standard' values" } } @@ -49,9 +55,18 @@ Test 'script declares SkipMakerProfile parameter (back-compat)' { } } -Test 'SkipMakerProfile forces InstallMode standard for back-compat' { - if ($src -notmatch 'if\s*\(\$SkipMakerProfile\)\s*\{\s*\$InstallMode\s*=\s*''standard''\s*\}') { - throw 'SkipMakerProfile back-compat coercion to InstallMode=standard not found' +Test 'SkipMakerProfile forces InstallMode developer for back-compat' { + if ($src -notmatch 'if\s*\(\$SkipMakerProfile\)\s*\{\s*\$InstallMode\s*=\s*''developer''\s*\}') { + throw 'SkipMakerProfile back-compat coercion to InstallMode=developer not found' + } +} + +Test 'legacy InstallMode values (lite/standard) are coerced to maker/developer' { + if ($src -notmatch "if\s*\(\`$InstallMode\s+-eq\s+'lite'\)\s*\{\s*\`$InstallMode\s*=\s*'maker'\s*\}") { + throw "legacy 'lite' -> 'maker' coercion missing" + } + if ($src -notmatch "if\s*\(\`$InstallMode\s+-eq\s+'standard'\)\s*\{\s*\`$InstallMode\s*=\s*'developer'\s*\}") { + throw "legacy 'standard' -> 'developer' coercion missing" } } @@ -114,7 +129,7 @@ Test 'bootstrap.ps1 does not pin SkipMakerProfile (consolidated installer prompt if ($bootstrapSrc -match 'SkipMakerProfile\s*=\s*\$true') { throw 'bootstrap.ps1 should not set SkipMakerProfile = $true after installer consolidation (ADO #7895603)' } - if ($bootstrapSrc -match "InstallMode\s*=\s*'(lite|standard)'") { + if ($bootstrapSrc -match "InstallMode\s*=\s*'(maker|developer|lite|standard)'") { throw "bootstrap.ps1 should not pin InstallMode; it should let the installer default to 'prompt' so VS Code asks the maker." } } @@ -130,12 +145,37 @@ Test 'bootstrap-lite.ps1 parses without errors' { if ($errors.Count -gt 0) { throw "Parse errors: $($errors[0].Message)" } } -Test 'bootstrap-lite.ps1 passes -InstallMode lite (back-compat shim)' { +Test 'bootstrap-lite.ps1 passes -InstallMode maker (back-compat shim for legacy URL)' { if ($liteSrc -match 'SkipMakerProfile\s*=\s*\$true') { throw 'bootstrap-lite.ps1 should not set SkipMakerProfile = $true' } - if ($liteSrc -notmatch "InstallMode\s*=\s*'lite'") { - throw "bootstrap-lite.ps1 should pin InstallMode = 'lite' so old links keep landing in lite mode" + if ($liteSrc -notmatch "InstallMode\s*=\s*'maker'") { + throw "bootstrap-lite.ps1 should pin InstallMode = 'maker' so old links keep landing in maker mode (was 'lite' before the rename)" + } +} + +Write-Host "`nbootstrap-dev.ps1:" -ForegroundColor Cyan + +$devPath = Join-Path $PSScriptRoot 'bootstrap-dev.ps1' +$devSrc = if (Test-Path $devPath) { Get-Content $devPath -Raw } else { '' } + +Test 'bootstrap-dev.ps1 exists' { + if (-not (Test-Path $devPath)) { + throw "bootstrap-dev.ps1 should exist as the shortcut for makers who want the Developer (default VS Code) experience" + } +} + +Test 'bootstrap-dev.ps1 parses without errors' { + if (-not $devSrc) { return } + $tokens = $null; $errors = $null + $null = [System.Management.Automation.Language.Parser]::ParseFile($devPath, [ref]$tokens, [ref]$errors) + if ($errors.Count -gt 0) { throw "Parse errors: $($errors[0].Message)" } +} + +Test 'bootstrap-dev.ps1 passes -InstallMode developer' { + if (-not $devSrc) { throw 'bootstrap-dev.ps1 does not exist' } + if ($devSrc -notmatch "InstallMode\s*=\s*'developer'") { + throw "bootstrap-dev.ps1 should pin InstallMode = 'developer'" } } @@ -270,11 +310,11 @@ Test 'envelope time is culture-invariant ISO-8601 (non-colon-separator locales)' } } Test 'installer telemetry emits an installMode dimension' { - Initialize-EssInstallTelemetry -Installer 'adk' -InstallMode 'lite' + Initialize-EssInstallTelemetry -Installer 'adk' -InstallMode 'maker' try { $data = Get-EssTelCommonData if (-not $data.ContainsKey('installMode')) { throw 'installMode dimension not emitted' } - if ($data.installMode -ne 'lite') { throw "installMode wrong: $($data.installMode)" } + if ($data.installMode -ne 'maker') { throw "installMode wrong: $($data.installMode)" } } finally { $script:EssTel.Ready = $false; $script:EssTel.Completed = $false } } @@ -286,26 +326,39 @@ Test 'installer telemetry defaults installMode to prompt when unspecified' { } finally { $script:EssTel.Ready = $false; $script:EssTel.Completed = $false } } -Test 'lite installer identity is instrumented (post-consolidation)' { +Test 'installer telemetry accepts legacy lite/standard values (back-compat)' { + # Old bootstrap-lite invocations may still pass -InstallMode lite until + # they resync. The emitter must accept those without validation errors. + Initialize-EssInstallTelemetry -Installer 'adk' -InstallMode 'lite' + try { + if ($script:EssTel.InstallMode -ne 'lite') { throw "legacy 'lite' should be accepted verbatim" } + } finally { $script:EssTel.Ready = $false; $script:EssTel.Completed = $false } + Initialize-EssInstallTelemetry -Installer 'adk' -InstallMode 'standard' + try { + if ($script:EssTel.InstallMode -ne 'standard') { throw "legacy 'standard' should be accepted verbatim" } + } finally { $script:EssTel.Ready = $false; $script:EssTel.Completed = $false } +} + +Test 'maker installer identity is instrumented (post-consolidation)' { # Regression: pre-consolidation the emitter guarded out Installer='lite' # because the lite installer was slated for removal. With bootstrap-lite.ps1 - # now a compat shim into the unified installer that passes -InstallMode lite, - # the guard would drop all lite-shim events. It must be gone. + # now a compat shim into the unified installer that passes -InstallMode maker, + # the guard would drop all shim events. It must be gone. $old = $env:ESS_ADK_TELEMETRY try { $env:ESS_ADK_TELEMETRY = '' - Initialize-EssInstallTelemetry -Installer 'lite' -InstallMode 'lite' - if (-not $script:EssTel.Ready) { throw 'lite installer should be telemetry-ready after consolidation' } + Initialize-EssInstallTelemetry -Installer 'lite' -InstallMode 'maker' + if (-not $script:EssTel.Ready) { throw 'legacy lite installer identity should be telemetry-ready after consolidation' } } finally { $env:ESS_ADK_TELEMETRY = $old; $script:EssTel.Ready = $false; $script:EssTel.Completed = $false } } -Test 'PowerShell emitter no longer guards out the lite installer' { +Test 'PowerShell emitter no longer guards out the legacy lite installer' { $emitterSrc = Get-Content $psEmitter -Raw if ($emitterSrc -match "if\s*\(\`$Installer\s+-eq\s+'lite'\)\s*\{\s*\`$script:EssTel\.Ready\s*=\s*\`$false") { throw 'PS emitter still guards out lite installer; guard should be removed after consolidation (ADO #7895603)' } } -Test 'bash emitter still guards out the lite installer (macOS scope unchanged in this iteration)' { - # macOS consolidation is out of scope for ADO #7895603. Until a follow-up +Test 'bash emitter still guards out the legacy lite installer (macOS scope unchanged in this iteration)' { + # macOS consolidation is out of scope for the current PR. Until a follow-up # US addresses macOS, bootstrap-lite-mac.sh remains a separate installer # tagged as ess_tel_installer=lite and the bash emitter still gates it out. $shSrc = Get-Content $shEmitter -Raw @@ -328,14 +381,41 @@ Test 'install-ess-adk.sh step() emits a per-step telemetry event' { if ($macInstaller -notmatch 'ess_tel_step' -or $macInstaller -notmatch 'ess_step_key') { throw 'step hook missing' } } -foreach ($bs in @('bootstrap.ps1', 'bootstrap-flightcheck.ps1', 'bootstrap-lite.ps1')) { +Test 'install-ess-adk.sh honors INSTALL_MODE (maker|developer|prompt) with legacy SKIP_MAKER_PROFILE alias' { + if ($macInstaller -notmatch 'INSTALL_MODE=') { throw 'INSTALL_MODE env var not consumed' } + if ($macInstaller -notmatch 'SKIP_MAKER_PROFILE.*==.*"true"[\s\S]{0,120}INSTALL_MODE="developer"') { + throw 'legacy SKIP_MAKER_PROFILE=true should map to INSTALL_MODE=developer' + } + if ($macInstaller -notmatch 'INSTALL_MODE.*==.*"lite"[\s\S]{0,80}INSTALL_MODE="maker"') { + throw "legacy 'lite' should be coerced to 'maker' on macOS" + } + if ($macInstaller -notmatch 'INSTALL_MODE.*==.*"standard"[\s\S]{0,80}INSTALL_MODE="developer"') { + throw "legacy 'standard' should be coerced to 'developer' on macOS" + } + if ($macInstaller -notmatch 'INSTALL_MODE"?\s*==\s*"developer"') { throw 'developer launch branch missing' } + if ($macInstaller -notmatch 'INSTALL_MODE"?\s*==\s*"maker"') { throw 'maker launch branch missing' } +} + +Test 'bootstrap-dev-mac.sh exists and pins INSTALL_MODE=developer' { + $devMacPath = Join-Path $PSScriptRoot 'bootstrap-dev-mac.sh' + if (-not (Test-Path $devMacPath)) { throw 'bootstrap-dev-mac.sh missing' } + $devMacSrc = Get-Content $devMacPath -Raw + if ($devMacSrc -notmatch 'INSTALL_MODE="developer"') { throw 'bootstrap-dev-mac.sh should pin INSTALL_MODE=developer' } +} + +Test 'bootstrap-lite-mac.sh pins INSTALL_MODE=maker (back-compat shim)' { + $liteMacSrc = Get-Content (Join-Path $PSScriptRoot 'bootstrap-lite-mac.sh') -Raw + if ($liteMacSrc -notmatch 'INSTALL_MODE="maker"') { throw 'bootstrap-lite-mac.sh should pin INSTALL_MODE=maker so legacy URL still lands in the chat-first experience' } +} + +foreach ($bs in @('bootstrap.ps1', 'bootstrap-flightcheck.ps1', 'bootstrap-lite.ps1', 'bootstrap-dev.ps1')) { $bsSrc = Get-Content (Join-Path $PSScriptRoot $bs) -Raw Test "$bs downloads the telemetry lib and sets ESS_INSTALL_TELEMETRY_LIB" { if ($bsSrc -notmatch 'install-telemetry\.ps1') { throw 'telemetry lib not downloaded' } if ($bsSrc -notmatch 'ESS_INSTALL_TELEMETRY_LIB') { throw 'env var not set' } } } -foreach ($bs in @('bootstrap-mac.sh', 'bootstrap-flightcheck-mac.sh', 'bootstrap-lite-mac.sh')) { +foreach ($bs in @('bootstrap-mac.sh', 'bootstrap-flightcheck-mac.sh', 'bootstrap-lite-mac.sh', 'bootstrap-dev-mac.sh')) { $bsSrc = Get-Content (Join-Path $PSScriptRoot $bs) -Raw Test "$bs downloads the telemetry lib and sets ESS_INSTALL_TELEMETRY_LIB" { if ($bsSrc -notmatch 'install-telemetry\.sh') { throw 'telemetry lib not downloaded' } diff --git a/setup/Install-EssAdk.ps1 b/setup/Install-EssAdk.ps1 index a0c2eb253..fcde2e5b2 100644 --- a/setup/Install-EssAdk.ps1 +++ b/setup/Install-EssAdk.ps1 @@ -54,16 +54,18 @@ .local/config.json so FlightCheck can authenticate without running /setup. .PARAMETER InstallMode - Selects the VS Code experience: 'lite' (chat-first, hidden developer - chrome), 'standard' (default VS Code layout with /setup injection), or - 'prompt' (default: let the ESS Maker Profile extension ask the maker - inside VS Code on first launch, default to standard if dismissed). The - ESS Maker Profile extension is installed in every mode; only the layout - and /setup delivery differ. Explicit values are respected without a - prompt; 'prompt' is the recommended default for new customers. + Selects the VS Code experience: 'maker' (chat-first, hidden developer + chrome - was 'lite'), 'developer' (default VS Code layout with /setup + injection - was 'standard'), or 'prompt' (default: let the ESS Maker + Profile extension ask the maker inside VS Code on first launch, default + to 'maker' if dismissed). The ESS Maker Profile extension is installed + in every mode; only the layout and /setup delivery differ. Explicit + values are respected without a prompt; 'prompt' is the recommended + default for new customers. The legacy values 'lite' and 'standard' are + still accepted and coerced to 'maker' and 'developer' respectively. .PARAMETER SkipMakerProfile - Back-compat switch. Equivalent to -InstallMode standard. Retained so + Back-compat switch. Equivalent to -InstallMode developer. Retained so existing bootstrap.ps1 invocations and CI scripts keep working; new callers should use -InstallMode instead. @@ -91,15 +93,22 @@ param( [switch] $SkipLaunch, [switch] $UseDsc, [switch] $FlightCheckOnly, - [ValidateSet('lite', 'standard', 'prompt')] + [ValidateSet('maker', 'developer', 'prompt', 'lite', 'standard')] [string] $InstallMode = 'prompt', [switch] $SkipMakerProfile ) -# Back-compat: -SkipMakerProfile forces standard mode even when +# Back-compat: -SkipMakerProfile forces developer mode even when # -InstallMode is passed. This preserves the old behaviour where the # switch was the only way to say "no chat-first layout". -if ($SkipMakerProfile) { $InstallMode = 'standard' } +if ($SkipMakerProfile) { $InstallMode = 'developer' } + +# Back-compat: legacy value aliases from the pre-rename installer +# (bootstrap-lite.ps1 previously pinned 'lite'; -SkipMakerProfile alias +# previously coerced to 'standard'). Coerce to the new canonical names +# so every downstream reference sees maker|developer|prompt. +if ($InstallMode -eq 'lite') { $InstallMode = 'maker' } +if ($InstallMode -eq 'standard') { $InstallMode = 'developer' } # Canonical mode label used throughout this script for both telemetry and # the VS Code settings write. Kept in $modeLabel so all downstream @@ -984,8 +993,8 @@ if (-not $FlightCheckOnly) { # Skipped in FlightCheckOnly mode (no VS Code launch) and when the user # passes -SkipExtensions (IT-locked-down boxes that block VSIX installs). if (-not $FlightCheckOnly -and -not $SkipExtensions) { - # Install the ESS Maker Profile extension in every mode. In lite mode - # it applies the chat-first layout; in standard mode it only handles + # Install the ESS Maker Profile extension in every mode. In maker mode + # it applies the chat-first layout; in developer mode it only handles # /setup injection after the welcome wizard closes (no visual # changes); in prompt mode it asks the maker inside VS Code on first # launch and applies whichever mode they pick. $modeLabel is set in @@ -1044,9 +1053,9 @@ if (-not $FlightCheckOnly -and -not $SkipExtensions) { } # Write the mode setting so the extension knows whether to apply - # the lite layout, inject /setup (standard mode), or prompt the - # maker inside VS Code on first launch (prompt mode -> empty - # string, which the extension treats as "not decided"). + # the maker (chat-first) layout, inject /setup (developer mode), + # or prompt the maker inside VS Code on first launch (prompt mode + # -> empty string, which the extension treats as "not decided"). # Uses string manipulation to preserve JSONC comments in settings.json. $settingsDir = Join-Path $env:APPDATA 'Code\User' if (-not (Test-Path $settingsDir)) { New-Item -ItemType Directory -Path $settingsDir -Force | Out-Null } @@ -1429,18 +1438,18 @@ if (-not $SkipLaunch) { $codePath = if ($code.Source) { $code.Source } elseif ($code.FullName) { $code.FullName } else { $null } if ($codePath) { # Launch strategy depends on mode: - # - Lite mode: just open the workspace. The ESS Maker Profile extension + # - Maker mode: just open the workspace. The ESS Maker Profile extension # handles layout + /setup injection after the welcome wizard closes. - # - Standard mode: use `code chat '/setup'` which opens Copilot Chat in + # - Developer mode: use `code chat '/setup'` which opens Copilot Chat in # the sidebar panel on the right (the standard chat experience). # - Prompt mode: open the workspace. The ESS Maker Profile extension # asks the maker which experience they want on first launch, then - # applies the chosen mode's flow (layout+injection for lite, no - # layout change + chat open for standard). + # applies the chosen mode's flow (layout+injection for maker, no + # layout change + chat open for developer). Push-Location $workspace try { - if ($modeLabel -eq 'standard') { - # Standard mode - use code chat to open /setup in sidebar panel + if ($modeLabel -eq 'developer') { + # Developer mode - use code chat to open /setup in sidebar panel Write-Step 'Opening workspace in VS Code and requesting /setup in Copilot Chat' $chatOutput = Invoke-Native { & $codePath chat '/setup' } $chatExit = $LASTEXITCODE @@ -1456,8 +1465,8 @@ if (-not $SkipLaunch) { Write-Host "If VS Code prompts you to trust the workspace or sign in to GitHub/Copilot, accept those prompts and /setup will run." -ForegroundColor Yellow Write-Host "If /setup does not start after trust/sign-in, open Copilot Chat manually and run /setup." -ForegroundColor Yellow } - } elseif ($modeLabel -eq 'lite') { - # Lite mode - extension handles /setup after welcome wizard + } elseif ($modeLabel -eq 'maker') { + # Maker mode - extension handles /setup after welcome wizard Write-Step 'Opening workspace in VS Code' Start-Process -FilePath $codePath -ArgumentList @('.') | Out-Null Write-Ok "Launched VS Code at $workspace" @@ -1469,7 +1478,7 @@ if (-not $SkipLaunch) { Write-Step 'Opening workspace in VS Code' Start-Process -FilePath $codePath -ArgumentList @('.') | Out-Null Write-Ok "Launched VS Code at $workspace" - Write-Host "VS Code will ask you to pick a chat-first (lite) or standard developer experience on first launch." -ForegroundColor Yellow + Write-Host "VS Code will ask you to pick a chat-first (maker) or default developer experience on first launch." -ForegroundColor Yellow Write-Host "If VS Code prompts you to trust the workspace, accept the prompt." -ForegroundColor Yellow } } finally { Pop-Location } diff --git a/setup/README.md b/setup/README.md index b24718520..94770b28f 100644 --- a/setup/README.md +++ b/setup/README.md @@ -14,34 +14,44 @@ iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-mac.sh)" ``` -Once complete, VS Code opens at `solutions/ess-maker-skills/` and — on the first launch — you'll be asked to pick **Standard** (default developer view with Copilot Chat in the side panel) or **Lite** (chat-first, big-button layout). Standard is the recommended default and will be applied if you dismiss the prompt. `/setup` is then automatically requested in Copilot Chat. You'll be prompted to trust the workspace and sign in to GitHub/Copilot — accept these prompts and `/setup` will connect the workspace to an existing editable DA Dev agent. +Once complete, VS Code opens at `solutions/ess-maker-skills/` and - on the first launch - you'll be asked to pick **Maker** (chat-first, big-button layout - recommended) or **Developer** (default VS Code view with Copilot Chat in the side panel). Maker is the recommended default and will be applied if you dismiss the prompt. `/setup` is then automatically requested in Copilot Chat. You'll be prompted to trust the workspace and sign in to GitHub/Copilot - accept these prompts and `/setup` will connect the workspace to an existing editable DA Dev agent. > **GitHub Copilot subscription is required** for the in-editor maker experience. This script installs the toolchain and extension scaffolding; it does not grant the Copilot entitlement. -## Lite Mode (Chat-First Layout) +## Maker Mode (Chat-First Layout) and Developer Mode -The one-shot Windows installer (`bootstrap.ps1`) will ask you to choose between Standard and Lite on first VS Code launch, so lite mode is a one-click choice from the main installer — no separate command needed. +The one-shot installer (`bootstrap.ps1` / `bootstrap-mac.sh`) asks you to choose between **Maker** and **Developer** on first VS Code launch, so mode selection is a one-click choice from the main installer - no separate command needed. Maker was previously called "Lite" and Developer was previously called "Standard"; the old names still work for pinned scripts and previously-installed users. -For scripts and docs that need to pin the choice up front (bypassing the in-VS-Code prompt), a lite-mode shortcut is retained: +For scripts and docs that need to pin the choice up front (bypassing the in-VS-Code prompt), mode-specific shortcuts are available: **Windows** (PowerShell): ```powershell +# Maker mode (chat-first) iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-lite.ps1) + +# Developer mode (default VS Code layout) +iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-dev.ps1) ``` **macOS** (Terminal): ```bash +# Maker mode (chat-first) /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-lite-mac.sh)" + +# Developer mode (default VS Code layout) +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-dev-mac.sh)" ``` -Lite mode is the same install as standard plus the **ESS Maker Profile** extension applying: +> `bootstrap-lite.ps1` / `bootstrap-lite-mac.sh` are kept at their original URLs for back-compat with existing docs and links, and now pin **Maker** mode (the renamed Lite mode). + +Maker mode is the same install as Developer mode plus the **ESS Maker Profile** extension applying: - A chat-only layout with all developer surfaces hidden - Big-button "Quick Actions" rail for common tasks (Connect, Customize landing page, Create, Scan, FlightCheck, Push) - A built-in tutorial explaining each button -You can switch between lite mode and standard VS Code at any time using the toggle buttons in the Quick Actions panel. +You can switch between Maker mode and Developer mode at any time using the toggle buttons in the Quick Actions panel. ## GitHub Codespaces (no local install) @@ -122,11 +132,13 @@ cd ~/source/Employee-Self-Service-Agent-Developer-Kit/solutions/ess-maker-skills |---|---| | `Install-EssAdk.ps1` | Windows orchestrator. Installs toolchain via winget, pip dependencies, clones repo, installs extensions, launches VS Code. With `-FlightCheckOnly`, installs minimal toolchain and runs FlightCheck. | | `install-ess-adk.sh` | macOS orchestrator. Same as above but uses Homebrew. Set `FLIGHTCHECK_ONLY=true` for FlightCheck-only mode. | -| `bootstrap.ps1` | Windows one-liner entry point (standard VS Code layout). | -| `bootstrap-lite.ps1` | Windows one-liner entry point (lite mode — chat-first layout). | +| `bootstrap.ps1` | Windows one-liner entry point (prompts for Maker or Developer on first VS Code launch). | +| `bootstrap-lite.ps1` | Windows one-liner entry point (Maker mode - chat-first layout; previously named "Lite"). | +| `bootstrap-dev.ps1` | Windows one-liner entry point (Developer mode - default VS Code layout; previously named "Standard"). | | `bootstrap-flightcheck.ps1` | Windows one-liner entry point (FlightCheck only). | -| `bootstrap-mac.sh` | macOS one-liner entry point (standard VS Code layout). | -| `bootstrap-lite-mac.sh` | macOS one-liner entry point (lite mode — chat-first layout). | +| `bootstrap-mac.sh` | macOS one-liner entry point (prompts for Maker or Developer on first VS Code launch). | +| `bootstrap-lite-mac.sh` | macOS one-liner entry point (Maker mode - chat-first layout; previously named "Lite"). | +| `bootstrap-dev-mac.sh` | macOS one-liner entry point (Developer mode - default VS Code layout; previously named "Standard"). | | `bootstrap-flightcheck-mac.sh` | macOS one-liner entry point (FlightCheck only). | | `ess-adk-setup.winget.yaml` | Declarative DSC config consumed by `winget configure` (optional Windows path). | | `telemetry/install-telemetry.ps1` | Installer telemetry emitter (PowerShell). Fail-open; emits install start/step/completion to Aria/1DS. | @@ -221,7 +233,9 @@ powershell -NoProfile -ExecutionPolicy Bypass -File .\Install-EssAdk.ps1 -SkipEx powershell -NoProfile -ExecutionPolicy Bypass -File .\Install-EssAdk.ps1 -SkipClone # skip git clone (toolchain only) powershell -NoProfile -ExecutionPolicy Bypass -File .\Install-EssAdk.ps1 -SkipLaunch # don't open VS Code at the end powershell -NoProfile -ExecutionPolicy Bypass -File .\Install-EssAdk.ps1 -FlightCheckOnly # minimal install for FlightCheck only -powershell -NoProfile -ExecutionPolicy Bypass -File .\Install-EssAdk.ps1 -SkipMakerProfile # standard VS Code (no lite mode) +powershell -NoProfile -ExecutionPolicy Bypass -File .\Install-EssAdk.ps1 -InstallMode maker # pin Maker mode (chat-first) +powershell -NoProfile -ExecutionPolicy Bypass -File .\Install-EssAdk.ps1 -InstallMode developer # pin Developer mode (default VS Code layout) +powershell -NoProfile -ExecutionPolicy Bypass -File .\Install-EssAdk.ps1 -SkipMakerProfile # legacy alias: coerced to Developer mode ``` For air-gapped / locked-down environments, IT can mirror the files internally and serve them from an intranet URL by passing `-SourceBaseUrl`. @@ -231,11 +245,17 @@ For air-gapped / locked-down environments, IT can mirror the files internally an From this folder: ```bash -# Full installer: +# Full installer (prompts for Maker or Developer on first VS Code launch): bash install-ess-adk.sh -# Full installer with lite mode (chat-first layout): -SKIP_MAKER_PROFILE=false bash install-ess-adk.sh +# Pin Maker mode (chat-first layout): +INSTALL_MODE=maker bash install-ess-adk.sh + +# Pin Developer mode (default VS Code layout): +INSTALL_MODE=developer bash install-ess-adk.sh + +# Legacy alias (still accepted; coerced to INSTALL_MODE=developer): +SKIP_MAKER_PROFILE=true bash install-ess-adk.sh # FlightCheck only: FLIGHTCHECK_ONLY=true bash install-ess-adk.sh diff --git a/setup/bootstrap-dev-mac.sh b/setup/bootstrap-dev-mac.sh new file mode 100644 index 000000000..0843d9d70 --- /dev/null +++ b/setup/bootstrap-dev-mac.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +# --------------------------------------------------------------------------- +# ESS ADK - macOS Bootstrap (Developer Mode) +# +# One-liner entry point: +# /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-dev-mac.sh)" +# +# Installs the full maker kit and lands the maker in the default VS Code +# layout (activity bar, file explorer, status bar visible) with /setup +# injected into Copilot Chat. Shortcut for makers who already know they want +# the Developer experience and want to skip the in-VS-Code mode prompt. +# --------------------------------------------------------------------------- +set -euo pipefail + +# Developer mode: pin INSTALL_MODE so install-ess-adk.sh uses `code chat` +# to open /setup in the sidebar panel and does not apply the chat-first +# layout. +export INSTALL_MODE="developer" + +# Parse optional --branch / --source-base-url arguments +BRANCH="main" +SOURCE_BASE_URL="" +while [[ $# -gt 0 ]]; do + case "$1" in + --branch) BRANCH="$2"; shift 2 ;; + --source-base-url) SOURCE_BASE_URL="$2"; shift 2 ;; + *) shift ;; + esac +done +SOURCE_BASE_URL="${SOURCE_BASE_URL:-${ESS_ADK_SOURCE_URL:-https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/$BRANCH/setup}}" + +TEMP_DIR="$(mktemp -d)" +trap 'rm -rf "$TEMP_DIR"' EXIT + +echo "Fetching ESS ADK installer to $TEMP_DIR" +INSTALLER_URL="$SOURCE_BASE_URL/install-ess-adk.sh" +echo " $INSTALLER_URL" + +if ! curl -fsSL "$INSTALLER_URL" -o "$TEMP_DIR/install-ess-adk.sh"; then + echo " [ERR] Failed to download: $INSTALLER_URL" >&2 + echo " If raw.githubusercontent.com is blocked by your firewall/proxy," >&2 + echo " clone the repo manually and run: INSTALL_MODE=developer setup/install-ess-adk.sh" >&2 + exit 1 +fi + +# Verify the downloaded file looks like a valid script +if [[ ! -s "$TEMP_DIR/install-ess-adk.sh" ]] || ! head -1 "$TEMP_DIR/install-ess-adk.sh" | grep -q '^#!/'; then + echo " [ERR] Downloaded file appears invalid (empty or not a shell script)" >&2 + echo " A corporate proxy may be intercepting the request." >&2 + exit 1 +fi + +# Best-effort: fetch the installer telemetry emitter (fail-open - a telemetry +# download failure must never block the install). +if curl -fsSL "$SOURCE_BASE_URL/telemetry/install-telemetry.sh" -o "$TEMP_DIR/install-telemetry.sh" 2>/dev/null; then + export ESS_INSTALL_TELEMETRY_LIB="$TEMP_DIR/install-telemetry.sh" +fi + +# Run the downloaded installer in a subshell to avoid issues if it calls exit +export ESS_ADK_BRANCH="$BRANCH" +bash "$TEMP_DIR/install-ess-adk.sh" diff --git a/setup/bootstrap-dev.ps1 b/setup/bootstrap-dev.ps1 new file mode 100644 index 000000000..b0bf5c639 --- /dev/null +++ b/setup/bootstrap-dev.ps1 @@ -0,0 +1,92 @@ +<# +.SYNOPSIS + One-liner bootstrap that pins the installer to Developer Mode. + +.DESCRIPTION + Downloads the installer and runs it with -InstallMode developer, giving + the maker the default VS Code layout (activity bar, file explorer, + status bar visible) plus automatic /setup injection into the Copilot + Chat side panel. This is the shortcut for makers who already know they + want the developer experience and want to skip the in-VS-Code mode + prompt on first launch. + + New customers should use bootstrap.ps1, which prompts inside VS Code + on first launch and defaults to Maker (chat-first) mode. + + iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-dev.ps1) + + All real work happens in Install-EssAdk.ps1; this file just gets the bits + onto the customer's machine and pins the mode to developer. + +.PARAMETER InstallRoot + Forwarded to Install-EssAdk.ps1. See that script for details. + +.PARAMETER Branch + Forwarded to Install-EssAdk.ps1. Defaults to "main". + +.PARAMETER SourceBaseUrl + Where to fetch the installer files from. Defaults to the raw GitHub URL of + the setup folder. Override for testing. +#> + +[CmdletBinding()] +param( + [string] $InstallRoot, + [string] $Branch = 'main', + [string] $SourceBaseUrl = 'https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup' +) + +$ErrorActionPreference = 'Stop' +[Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 + +$tempDir = Join-Path $env:TEMP "ess-adk-bootstrap-$([Guid]::NewGuid().ToString('N').Substring(0,8))" +New-Item -ItemType Directory -Path $tempDir -Force | Out-Null + +$files = @( + 'ess-adk-setup.winget.yaml', + 'Install-EssAdk.ps1' +) + +Write-Host "Fetching ESS ADK bootstrap files to $tempDir" -ForegroundColor Cyan +foreach ($f in $files) { + $url = "$SourceBaseUrl/$f" + $dst = Join-Path $tempDir $f + Write-Host " $url" + try { + Invoke-WebRequest -Uri $url -OutFile $dst -UseBasicParsing -TimeoutSec 60 + } catch { + Write-Host " [ERR] Failed to download: $url" -ForegroundColor Red + Write-Host " If raw.githubusercontent.com is blocked by your firewall/proxy," -ForegroundColor Yellow + Write-Host " download the repo manually and run:" -ForegroundColor Yellow + Write-Host " .\setup\Install-EssAdk.ps1 -InstallMode developer" -ForegroundColor Yellow + throw $_ + } +} + +# Best-effort: fetch the installer telemetry emitter (fail-open - a telemetry +# download failure must never block the install). +$telLib = Join-Path $tempDir 'install-telemetry.ps1' +try { + Invoke-WebRequest -Uri "$SourceBaseUrl/telemetry/install-telemetry.ps1" -OutFile $telLib -UseBasicParsing -TimeoutSec 30 + $env:ESS_INSTALL_TELEMETRY_LIB = $telLib +} catch { + Write-Host " [warn] Installer telemetry unavailable (continuing)" -ForegroundColor DarkYellow +} + +$installer = Join-Path $tempDir 'Install-EssAdk.ps1' + +# Run the installer in-memory (as a script block) so execution policy never +# applies - the script content is never "executed from disk". Read as UTF-8 +# explicitly: Windows PowerShell 5.1 otherwise decodes a no-BOM file as ANSI +# (CP1252), which mangles any non-ASCII byte and breaks ScriptBlock parsing. +$scriptContent = [System.IO.File]::ReadAllText($installer, [System.Text.Encoding]::UTF8) +$scriptBlock = [ScriptBlock]::Create($scriptContent) + +# Developer mode: pass -InstallMode developer so the installer skips the +# in-VS-Code mode prompt and lands directly in the default VS Code layout +# with /setup requested via `code chat`. Same physical installer as the +# other bootstraps; just a different pinned mode. +$installerArgs = @{ Branch = $Branch; InstallMode = 'developer' } +if ($InstallRoot) { $installerArgs.InstallRoot = $InstallRoot } + +& $scriptBlock @installerArgs diff --git a/setup/bootstrap-lite-mac.sh b/setup/bootstrap-lite-mac.sh index e33e0bb93..359f8369a 100644 --- a/setup/bootstrap-lite-mac.sh +++ b/setup/bootstrap-lite-mac.sh @@ -1,16 +1,24 @@ #!/usr/bin/env bash # --------------------------------------------------------------------------- -# ESS ADK — macOS Bootstrap (Lite Mode) +# ESS ADK - macOS Bootstrap (Maker Mode - formerly "Lite Mode") # -# One-liner entry point: +# One-liner entry point (kept as-is for URL back-compat): # /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-lite-mac.sh)" # # Installs the full maker kit with the ESS Maker Profile extension enabled, -# giving users a chat-first, big-button experience. +# giving users a chat-first, big-button experience (Maker mode). This is the +# macOS equivalent of the Windows bootstrap-lite.ps1 compat shim: same +# unified install-ess-adk.sh script, mode pinned to 'maker' up front. # --------------------------------------------------------------------------- set -euo pipefail -# Lite mode: do NOT set SKIP_MAKER_PROFILE so the chat-first profile installs. +# Maker mode: pin INSTALL_MODE so install-ess-adk.sh skips the mode-prompt +# path and lands the maker in the chat-first layout. Legacy telemetry +# gating: the bash emitter still guards out ESS_TEL_INSTALLER=lite until +# macOS consolidation ships, so we opt this shim out of the new 'adk' +# installer identity by exporting the override. +export INSTALL_MODE="maker" +export ESS_TEL_INSTALLER_OVERRIDE="lite" # Parse optional --branch / --source-base-url arguments BRANCH="main" diff --git a/setup/bootstrap-lite.ps1 b/setup/bootstrap-lite.ps1 index 9ac963a16..24a03961b 100644 --- a/setup/bootstrap-lite.ps1 +++ b/setup/bootstrap-lite.ps1 @@ -1,12 +1,14 @@ <# .SYNOPSIS - Back-compat one-liner bootstrap that pins the installer to Lite Mode. + Back-compat one-liner bootstrap that pins the installer to Maker Mode + (formerly known as "Lite Mode"). .DESCRIPTION - Downloads the installer and runs it with -InstallMode lite. Kept so + Downloads the installer and runs it with -InstallMode maker. Kept so existing links (docs, blog posts, share sheets) that point at bootstrap-lite.ps1 keep working after the standard + lite installers - were merged into a single bootstrap.ps1. + were merged into a single bootstrap.ps1 and the modes were renamed + from lite/standard to maker/developer. New customers should use bootstrap.ps1, which prompts inside VS Code on first launch to pick the experience. This shim is documented as @@ -15,7 +17,8 @@ iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-lite.ps1) All real work happens in Install-EssAdk.ps1; this file just gets the bits - onto the customer's machine and pins the mode to lite. + onto the customer's machine and pins the mode to maker (the chat-first + experience). .PARAMETER InstallRoot Forwarded to Install-EssAdk.ps1. See that script for details. @@ -81,10 +84,11 @@ $installer = Join-Path $tempDir 'Install-EssAdk.ps1' $scriptContent = [System.IO.File]::ReadAllText($installer, [System.Text.Encoding]::UTF8) $scriptBlock = [ScriptBlock]::Create($scriptContent) -# Lite mode: pass -InstallMode lite so the ESS Maker Profile applies the -# chat-first layout without asking the maker. Kept as a compat shim while -# the single bootstrap.ps1 becomes the recommended entry point. -$installerArgs = @{ Branch = $Branch; InstallMode = 'lite' } +# Maker mode (was "Lite mode" before the rename): pass -InstallMode maker +# so the ESS Maker Profile applies the chat-first layout without asking the +# maker. Kept as a compat shim while the single bootstrap.ps1 becomes the +# recommended entry point. +$installerArgs = @{ Branch = $Branch; InstallMode = 'maker' } if ($InstallRoot) { $installerArgs.InstallRoot = $InstallRoot } & $scriptBlock @installerArgs diff --git a/setup/install-ess-adk.sh b/setup/install-ess-adk.sh index 6ca8ed0af..ce0be8135 100644 --- a/setup/install-ess-adk.sh +++ b/setup/install-ess-adk.sh @@ -22,7 +22,31 @@ set -euo pipefail BRANCH="${ESS_ADK_BRANCH:-main}" INSTALL_ROOT="${ESS_ADK_INSTALL_ROOT:-$HOME/source}" FLIGHTCHECK_ONLY="${FLIGHTCHECK_ONLY:-false}" +# INSTALL_MODE: maker | developer | prompt (or legacy lite | standard). +# 'maker' (was 'lite') - chat-first layout, /setup after welcome wizard +# 'developer' (was 'standard') - default VS Code layout, /setup via `code chat` +# 'prompt' - let the ESS Maker Profile ask on first launch +# Legacy env var SKIP_MAKER_PROFILE=true is still accepted and mapped to +# INSTALL_MODE=developer for back-compat with pinned CI scripts and old docs. +INSTALL_MODE="${INSTALL_MODE:-}" SKIP_MAKER_PROFILE="${SKIP_MAKER_PROFILE:-false}" +if [[ -z "$INSTALL_MODE" && "$SKIP_MAKER_PROFILE" == "true" ]]; then + INSTALL_MODE="developer" +fi +if [[ -z "$INSTALL_MODE" ]]; then + INSTALL_MODE="prompt" +fi +# Coerce legacy names to the new canonical values so every downstream +# reference works with maker|developer|prompt. +if [[ "$INSTALL_MODE" == "lite" ]]; then INSTALL_MODE="maker"; fi +if [[ "$INSTALL_MODE" == "standard" ]]; then INSTALL_MODE="developer"; fi +case "$INSTALL_MODE" in + maker|developer|prompt) ;; + *) + echo "WARNING: unknown INSTALL_MODE '$INSTALL_MODE'; defaulting to prompt" >&2 + INSTALL_MODE="prompt" + ;; +esac REPO_URL="https://github.com/microsoft/Employee-Self-Service-Agent-Developer-Kit.git" REPO_NAME="Employee-Self-Service-Agent-Developer-Kit" OBJECT_MODEL_INSTALLER_PATH="$INSTALL_ROOT/$REPO_NAME/solutions/ess-maker-skills/scripts/install_agentbuilder_object_model.py" @@ -82,15 +106,20 @@ if ! declare -f ess_tel_init >/dev/null 2>&1; then ess_tel_complete() { :; } fi -# Installer identity: flightcheck | adk (full, maker profile skipped) | lite. +# Installer identity: flightcheck | adk (full, chosen mode carried by +# INSTALL_MODE dimension) | lite (legacy back-compat when the caller pinned +# SKIP_MAKER_PROFILE=false explicitly via the old bootstrap-lite-mac.sh). if [[ "$FLIGHTCHECK_ONLY" == "true" ]]; then _ess_installer=flightcheck -elif [[ "$SKIP_MAKER_PROFILE" == "true" ]]; then - _ess_installer=adk +elif [[ "${ESS_TEL_INSTALLER_OVERRIDE:-}" != "" ]]; then + # Explicit override from the legacy bootstrap-lite-mac.sh so its events + # keep the pre-consolidation `lite` installer tag (the bash emitter still + # gates that identity out until macOS consolidation ships). + _ess_installer="$ESS_TEL_INSTALLER_OVERRIDE" else - _ess_installer=lite + _ess_installer=adk fi -ess_tel_init "$_ess_installer" || true +ess_tel_init "$_ess_installer" "$INSTALL_MODE" || true # Emit a completion event on any exit (success on 0, cancelled on Ctrl+C/term, # failure otherwise). The FlightCheck-only path records success explicitly @@ -393,15 +422,13 @@ if [[ "$FLIGHTCHECK_ONLY" != "true" ]]; then fi done - # ESS Maker Profile — installs in both modes. In lite mode it - # applies the chat-first layout; in standard mode it only handles - # /setup injection (no visual changes). The mode is communicated - # via essMaker.mode in VS Code's user settings.json. - if [[ "$SKIP_MAKER_PROFILE" == "true" ]]; then - MODE_LABEL="standard" - else - MODE_LABEL="lite" - fi + # ESS Maker Profile - installs in every mode. In maker mode it + # applies the chat-first layout; in developer mode it only handles + # /setup injection (no visual changes); in prompt mode it asks the + # maker inside VS Code on first launch. The mode is communicated + # via essMaker.mode in VS Code's user settings.json (prompt maps + # to empty string, matching the Windows installer contract). + MODE_LABEL="$INSTALL_MODE" step "Installing ESS Maker Profile ($MODE_LABEL mode)" MAKER_VSIX_DIR="$REPO_PATH/tools/ess-maker-profile/extension" @@ -431,6 +458,14 @@ if [[ "$FLIGHTCHECK_ONLY" != "true" ]]; then fi mkdir -p "$SETTINGS_DIR" SETTINGS_FILE="$SETTINGS_DIR/settings.json" + # In prompt mode, write an empty string so the ESS Maker Profile + # extension knows to show the QuickPick on first VS Code launch + # (matches the Windows installer contract). + if [[ "$MODE_LABEL" == "prompt" ]]; then + SETTINGS_MODE_VALUE="" + else + SETTINGS_MODE_VALUE="$MODE_LABEL" + fi if [[ -f "$SETTINGS_FILE" ]]; then # Merge into existing settings using Python (available from step 2) python3 -c " @@ -440,12 +475,12 @@ try: settings = json.load(f) except: settings = {} -settings['essMaker.mode'] = '$MODE_LABEL' +settings['essMaker.mode'] = '$SETTINGS_MODE_VALUE' with open('$SETTINGS_FILE', 'w') as f: json.dump(settings, f, indent=2) " 2>/dev/null || true else - echo "{\"essMaker.mode\": \"$MODE_LABEL\"}" > "$SETTINGS_FILE" + echo "{\"essMaker.mode\": \"$SETTINGS_MODE_VALUE\"}" > "$SETTINGS_FILE" fi else warn "VS Code 'code' CLI not found. Install extensions manually after launching VS Code." @@ -690,11 +725,13 @@ fi # --------------------------------------------------------------------------- if [[ -n "$CODE_CMD" ]]; then # Launch strategy depends on mode: - # - Standard mode (SKIP_MAKER_PROFILE=true): use `code chat` to open - # /setup in the sidebar panel (the standard chat experience). - # - Lite mode: just open the workspace. The ESS Maker Profile extension + # - Developer mode: use `code chat` to open /setup in the sidebar panel + # (the default Copilot Chat experience). + # - Maker mode: just open the workspace. The ESS Maker Profile extension # handles layout + /setup injection after the welcome wizard closes. - if [[ "$SKIP_MAKER_PROFILE" == "true" ]]; then + # - Prompt mode: open the workspace. The extension asks which experience + # the maker wants on first launch, then applies the chosen mode. + if [[ "$INSTALL_MODE" == "developer" ]]; then step "Opening workspace in VS Code and requesting /setup in Copilot Chat" if (cd "$WORKSPACE_PATH" && "$CODE_CMD" chat "/setup"); then ok "Requested /setup in Copilot Chat at $WORKSPACE_PATH" @@ -706,7 +743,7 @@ if [[ -n "$CODE_CMD" ]]; then "$CODE_CMD" "$WORKSPACE_PATH" || warn "Could not launch VS Code. Open manually: $WORKSPACE_PATH" echo "Next: in VS Code, open Copilot Chat and run /setup to connect an editable DA Dev agent." fi - else + elif [[ "$INSTALL_MODE" == "maker" ]]; then step "Opening workspace in VS Code" if (cd "$WORKSPACE_PATH" && "$CODE_CMD" .); then ok "Launched VS Code at $WORKSPACE_PATH" @@ -716,6 +753,18 @@ if [[ -n "$CODE_CMD" ]]; then warn "Could not launch VS Code. Open manually: $WORKSPACE_PATH" echo "Next: in VS Code, open Copilot Chat and run /setup to connect an editable DA Dev agent." fi + else + # Prompt mode - extension will ask which experience to apply + # on first launch, then run the appropriate flow. + step "Opening workspace in VS Code" + if (cd "$WORKSPACE_PATH" && "$CODE_CMD" .); then + ok "Launched VS Code at $WORKSPACE_PATH" + echo -e " ${YELLOW}VS Code will ask you to pick a chat-first (Maker) or default developer experience on first launch.${NC}" + echo -e " ${YELLOW}If VS Code prompts you to trust the workspace, accept the prompt.${NC}" + else + warn "Could not launch VS Code. Open manually: $WORKSPACE_PATH" + echo "Next: in VS Code, open Copilot Chat and run /setup to connect an editable DA Dev agent." + fi fi else warn "Could not launch VS Code. Open manually: $WORKSPACE_PATH" diff --git a/setup/telemetry/install-telemetry.ps1 b/setup/telemetry/install-telemetry.ps1 index 73f6bb574..658243a56 100644 --- a/setup/telemetry/install-telemetry.ps1 +++ b/setup/telemetry/install-telemetry.ps1 @@ -224,15 +224,16 @@ function Initialize-EssInstallTelemetry { .SYNOPSIS Begin installer telemetry: notice, identity, and the start event. .PARAMETER Installer One of adk | lite | flightcheck. 'lite' is retained for the back-compat bootstrap-lite.ps1 shim; new callers should pass - 'adk' with -InstallMode lite instead. + 'adk' with -InstallMode maker instead. .PARAMETER InstallMode The VS Code experience the maker will land in - after the installer completes: lite | standard | prompt. 'prompt' - means the ESS Maker Profile extension will ask on first launch. + after the installer completes: maker | developer | prompt (or the + legacy aliases 'lite' | 'standard'). 'prompt' means the ESS Maker + Profile extension will ask on first launch. #> param( [ValidateSet('adk', 'lite', 'flightcheck')] [string]$Installer = 'adk', - [ValidateSet('lite', 'standard', 'prompt')] + [ValidateSet('maker', 'developer', 'prompt', 'lite', 'standard')] [string]$InstallMode = 'prompt' ) try { diff --git a/setup/telemetry/install-telemetry.sh b/setup/telemetry/install-telemetry.sh index 95606ea93..5b61fb731 100644 --- a/setup/telemetry/install-telemetry.sh +++ b/setup/telemetry/install-telemetry.sh @@ -135,7 +135,7 @@ ess_tel_send() { ms=$(( (10#$ns / 1000000) % 1000 )) ts="$(date -u +%Y-%m-%dT%H:%M:%S).$(printf '%03d' "$ms")Z" local data - data="{\"schemaVersion\":\"$ESS_TEL_SCHEMA\",\"env\":\"$ESS_TEL_ENV\",\"installer\":\"$ESS_TEL_INSTALLER\",\"invocationSource\":\"installer\",\"platform\":\"$ESS_TEL_PLATFORM\",\"os\":\"$(_ess_tel_jesc "$ESS_TEL_OS")\",\"instanceId\":\"$ESS_TEL_INSTANCE\",\"adkVersion\":\"$(_ess_tel_jesc "$ESS_TEL_ADKVER")\",\"firstRun\":$ESS_TEL_FIRSTRUN$extra}" + data="{\"schemaVersion\":\"$ESS_TEL_SCHEMA\",\"env\":\"$ESS_TEL_ENV\",\"installer\":\"$ESS_TEL_INSTALLER\",\"installMode\":\"$ESS_TEL_INSTALL_MODE\",\"invocationSource\":\"installer\",\"platform\":\"$ESS_TEL_PLATFORM\",\"os\":\"$(_ess_tel_jesc "$ESS_TEL_OS")\",\"instanceId\":\"$ESS_TEL_INSTANCE\",\"adkVersion\":\"$(_ess_tel_jesc "$ESS_TEL_ADKVER")\",\"firstRun\":$ESS_TEL_FIRSTRUN$extra}" local body="{\"ver\":\"4.0\",\"name\":\"$name\",\"time\":\"$ts\",\"iKey\":\"$envtoken\",\"data\":$data}" local uploadms; uploadms="$(( $(date +%s) * 1000 ))" # Circuit breaker: an unreachable/slow collector must not add its timeout to @@ -160,10 +160,16 @@ ess_tel_send() { # --- public API ------------------------------------------------------------ ess_tel_init() { # $1 = installer (adk|lite|flightcheck) + # $2 = install mode (maker|developer|prompt; or legacy lite|standard). + # Defaults to 'prompt' to match the Windows installer contract. ESS_TEL_INSTALLER="${1:-adk}" + ESS_TEL_INSTALL_MODE="${2:-prompt}" ess_tel_enabled || { ESS_TEL_READY=0; return 0; } - # The Lite-mode installer is being merged into the standard ADK installer - # (mode will become an onboarding prompt), so it is no longer instrumented. + # The legacy 'lite' installer identity (bootstrap-lite-mac.sh sets it + # explicitly via ESS_TEL_INSTALLER_OVERRIDE) is not instrumented yet; + # macOS consolidation is deferred to a follow-up US. The unified + # installer identity is 'adk' with the maker/developer/prompt mode + # carried by the installMode dimension. [[ "$ESS_TEL_INSTALLER" == "lite" ]] && { ESS_TEL_READY=0; return 0; } ess_tel_ensure_config local envname diff --git a/tools/ess-maker-profile/extension/CHANGELOG.md b/tools/ess-maker-profile/extension/CHANGELOG.md index a71d0be48..0be2bd1aa 100644 --- a/tools/ess-maker-profile/extension/CHANGELOG.md +++ b/tools/ess-maker-profile/extension/CHANGELOG.md @@ -1,13 +1,35 @@ # Changelog +## 0.4.27 (POC) + +- **Modes renamed: Lite -> Maker (default), Standard -> Developer.** + User research showed "Lite" undersold the mode (it is a full, + streamlined chat-first UX, not a cut-down one) and "Standard" was not + really standard (it still opens Copilot Chat and runs `/setup` on + first launch). The first-launch QuickPick now offers **Maker + (recommended)** and **Developer**, dismissing defaults to Maker, + and a new `setup/bootstrap-dev.ps1` shortcut mirrors the existing + `bootstrap-lite.ps1`. The `essMaker.mode` setting accepts the new + values (`maker`, `developer`) and still accepts the legacy values + (`lite`, `standard`) so pinned CI, docs, and previously-installed + users are not disrupted; legacy values are normalized on read at the + top of `firstInstallDispatch`. The `essMaker.restoreStandardLayout` + command ID is preserved (its user-facing title is now "Restore + Developer Layout"). The persisted on-disk key `essMaker.liteMode.v1` + is likewise preserved so existing users do not lose their layout + preference. macOS gets a matching `bootstrap-dev-mac.sh` shortcut; + `bootstrap-lite-mac.sh` continues to work and pins Maker mode. The + installer emits an additional `installMode` telemetry dimension + (maker | developer | prompt, or legacy lite | standard). + ## 0.4.26 (POC) - **First-launch mode prompt.** After the consolidated Windows installer - (see ADO #7895603 — one `bootstrap.ps1` for both experiences), the + (see ADO #7895603 - one `bootstrap.ps1` for both experiences), the extension asks the maker on first VS Code launch which experience they - want: **Standard (recommended)** — default developer view with GitHub - Copilot Chat in the side panel and automatic `/setup` — or **Lite - (chat-first)** — hidden developer chrome and the Quick Actions rail. + want: **Standard (recommended)** - default developer view with GitHub + Copilot Chat in the side panel and automatic `/setup` - or **Lite + (chat-first)** - hidden developer chrome and the Quick Actions rail. The choice is persisted to the `essMaker.mode` global setting so subsequent launches skip the prompt; dismissing the QuickPick defaults to Standard. Legacy invocations that pin `essMaker.mode` to `lite` or diff --git a/tools/ess-maker-profile/extension/extension.js b/tools/ess-maker-profile/extension/extension.js index ed34eed05..155004c78 100644 --- a/tools/ess-maker-profile/extension/extension.js +++ b/tools/ess-maker-profile/extension/extension.js @@ -19,6 +19,10 @@ function _log(msg) { const EXT_ID = 'microsoft-ess.ess-maker-profile'; const APPLIED_KEY = 'essMaker.chatOnlyApplied.v7'; +// Historical key name: stores whether the user wants the chat-only ("Maker +// mode") layout applied on activation. Kept as-is on disk (essMaker.liteMode.v1) +// to preserve existing users' persisted preference across the lite -> maker +// rename; renaming the storage key would silently reset everyone to the default. const LITE_MODE_KEY = 'essMaker.liteMode.v1'; const SETTINGS_BACKUP_KEY = 'essMaker.settingsBackup.v1'; @@ -255,7 +259,7 @@ async function tryRun(commandId, ...args) { catch (err) { console.warn(`[ess-maker] ${commandId} failed:`, err.message); return false; } } -function isLiteMode() { +function isMakerLayout() { const cfg = vscode.workspace.getConfiguration(); return cfg.get('workbench.activityBar.location') === 'hidden'; } @@ -821,7 +825,7 @@ async function restoreStandardLayout() { } const sel = await vscode.window.showInformationMessage( - 'ESS Maker: standard layout restored. Reload the window to see all changes.', + 'ESS Maker: Developer layout restored. Reload the window to see all changes.', 'Reload Window' ); if (sel === 'Reload Window') { @@ -894,7 +898,7 @@ class ActionsViewProvider { await tryRun('workbench.files.action.expandRecursively'); await this.refresh(); const sel = await vscode.window.showInformationMessage( - 'Standard layout restored. Reload the window for full effect.', + 'Developer layout restored. Reload the window for full effect.', 'Reload Window' ); if (sel === 'Reload Window') { @@ -905,7 +909,7 @@ class ActionsViewProvider { await applySettings(CHAT_ONLY_LAYOUT, vscode.ConfigurationTarget.Global); await this.refresh(); const sel = await vscode.window.showInformationMessage( - 'Lite mode applied. Reload the window for full effect.', + 'Maker mode applied. Reload the window for full effect.', 'Reload Window' ); if (sel === 'Reload Window') { @@ -937,9 +941,9 @@ class ActionsViewProvider { for (const a of ACTIONS) { states[a.id] = actionState(a, completed); } - const liteMode = isLiteMode(); + const makerLayout = isMakerLayout(); try { - await this._view.webview.postMessage({ type: 'state', states, liteMode }); + await this._view.webview.postMessage({ type: 'state', states, makerLayout }); } catch {} } @@ -1042,10 +1046,10 @@ class ActionsViewProvider {

Customize your ESS agent

${buttons}
- @@ -1063,10 +1067,10 @@ class ActionsViewProvider {
How each button works
- @@ -1116,14 +1120,14 @@ class ActionsViewProvider { } } // Show/hide mode-toggle buttons based on current layout. - const btnLite = document.getElementById('btn-lite'); - const btnStandard = document.getElementById('btn-standard'); - if (e.data.liteMode) { - btnLite.style.display = 'none'; - btnStandard.style.display = ''; + const btnMaker = document.getElementById('btn-maker'); + const btnDeveloper = document.getElementById('btn-developer'); + if (e.data.makerLayout) { + btnMaker.style.display = 'none'; + btnDeveloper.style.display = ''; } else { - btnLite.style.display = ''; - btnStandard.style.display = 'none'; + btnMaker.style.display = ''; + btnDeveloper.style.display = 'none'; } }); vscode.postMessage({ type: 'ready' }); @@ -1428,24 +1432,28 @@ async function maybePromptReinstall(repoRoot) { // --- First-install dispatch (ADO #7895603 consolidated installer) --------- // When the consolidated installer leaves essMaker.mode unset ("" or // "prompt"), we prompt the maker on their first VS Code launch to pick -// lite (chat-first) vs standard (developer view) and persist the choice -// to the essMaker.mode global setting so subsequent launches skip the -// prompt. This exists so the consolidated Windows installer doesn't need -// to interrupt the CLI with a mode question — the choice surfaces in +// maker (chat-first) vs developer (default VS Code view) and persist the +// choice to the essMaker.mode global setting so subsequent launches skip +// the prompt. This exists so the consolidated Windows installer doesn't +// need to interrupt the CLI with a mode question - the choice surfaces in // VS Code where the maker can preview the two options as they read them. +// +// Legacy value migration: the pre-rename installer wrote 'lite'/'standard' +// to essMaker.mode. Reads here normalize those to the new 'maker'/'developer' +// values so existing users keep the mode they picked. async function promptForInstallMode() { const picks = [ { - label: 'Standard (recommended)', - description: 'Default VS Code layout with GitHub Copilot Chat in the side panel', - detail: 'Best if you plan to inspect or edit files directly. /setup runs automatically.', - mode: 'standard', - }, - { - label: 'Lite (chat-first)', + label: 'Maker (recommended)', description: 'Hides file tree, tabs, and status bar; big-button Quick Actions rail', detail: 'Best if you mostly work in chat and want a focused HR/IT admin surface.', - mode: 'lite', + mode: 'maker', + }, + { + label: 'Developer', + description: 'Default VS Code layout with GitHub Copilot Chat in the side panel', + detail: 'Best if you plan to inspect or edit files directly. /setup runs automatically.', + mode: 'developer', }, ]; try { @@ -1455,15 +1463,24 @@ async function promptForInstallMode() { matchOnDescription: true, matchOnDetail: true, }); - return pick ? pick.mode : 'standard'; + return pick ? pick.mode : 'maker'; } catch (err) { _log(`promptForInstallMode: error ${err && err.message}`); - return 'standard'; + return 'maker'; } } +// Normalize legacy essMaker.mode values ('lite', 'standard') written by +// the pre-rename installer to the new canonical names, so existing users +// don't get re-prompted after upgrading the extension. +function normalizeInstallerMode(mode) { + if (mode === 'lite') return 'maker'; + if (mode === 'standard') return 'developer'; + return mode; +} + async function firstInstallDispatch(context, installerMode) { - let effectiveMode = installerMode; + let effectiveMode = normalizeInstallerMode(installerMode); if (!effectiveMode || effectiveMode === 'prompt') { effectiveMode = await promptForInstallMode(); try { @@ -1476,9 +1493,9 @@ async function firstInstallDispatch(context, installerMode) { _log(`firstInstallDispatch: failed to persist essMaker.mode: ${err && err.message}`); } } - const isStandardMode = effectiveMode === 'standard'; - _log(`firstInstallDispatch: effectiveMode=${effectiveMode}, isStandardMode=${isStandardMode}`); - context.globalState.update(LITE_MODE_KEY, !isStandardMode); + const isDeveloperMode = effectiveMode === 'developer'; + _log(`firstInstallDispatch: effectiveMode=${effectiveMode}, isDeveloperMode=${isDeveloperMode}`); + context.globalState.update(LITE_MODE_KEY, !isDeveloperMode); // Check if the user already has a config file (returning user who // re-ran the installer). Skip /setup if already configured. @@ -1491,26 +1508,26 @@ async function firstInstallDispatch(context, installerMode) { } _log(`firstInstallDispatch: alreadyConfigured=${alreadyConfigured}`); - if (isStandardMode) { - // Standard mode: no layout changes. In prompt mode the installer + if (isDeveloperMode) { + // Developer mode: no layout changes. In prompt mode the installer // could not eagerly run `code chat '/setup'` (mode was unknown at // launch time), so surface /setup here for the not-yet-configured - // path — otherwise a maker who picks Standard here would land in + // path - otherwise a maker who picks Developer here would land in // an empty chat with no cue what to do next. context.globalState.update(APPLIED_KEY, true); if (!alreadyConfigured) { - _log('firstInstallDispatch: standard mode, running /setup via injectSetup'); + _log('firstInstallDispatch: developer mode, running /setup via injectSetup'); waitForWelcomeWizard() .then(() => new Promise(r => setTimeout(r, 3000))) .then(() => injectSetup()) - .catch((err) => _log(`firstInstallDispatch: standard injectSetup error: ${err && err.message}`)); + .catch((err) => _log(`firstInstallDispatch: developer injectSetup error: ${err && err.message}`)); } else { - _log('firstInstallDispatch: standard mode, already configured — no action'); + _log('firstInstallDispatch: developer mode, already configured - no action'); } return; } - // Lite mode: apply layout. + // Maker mode: apply layout. applyChatOnlyLayout({ silent: false }) .then(() => context.globalState.update(APPLIED_KEY, true)) .catch(() => {}); @@ -1519,11 +1536,11 @@ async function firstInstallDispatch(context, installerMode) { setTimeout(() => tryRun('workbench.action.chat.open').catch(() => {}), 3000); } else { waitForWelcomeWizard() - .then(() => { _log('firstInstallDispatch: wizard done (lite), waiting 3s...'); return new Promise(r => setTimeout(r, 3000)); }) - .then(() => { _log('firstInstallDispatch: calling injectSetup (lite)'); return injectSetup(); }) - .then(() => _log('firstInstallDispatch: injectSetup completed (lite)')) + .then(() => { _log('firstInstallDispatch: wizard done (maker), waiting 3s...'); return new Promise(r => setTimeout(r, 3000)); }) + .then(() => { _log('firstInstallDispatch: calling injectSetup (maker)'); return injectSetup(); }) + .then(() => _log('firstInstallDispatch: injectSetup completed (maker)')) .catch((err) => { - _log(`firstInstallDispatch: ERROR in lite wizard chain: ${err && err.message}`); + _log(`firstInstallDispatch: ERROR in maker wizard chain: ${err && err.message}`); console.warn('[ess-maker] Welcome wizard wait timed out, skipping auto /setup'); }); } @@ -1575,19 +1592,20 @@ function activate(context) { startPrereqWatcher(context); // First-run vs subsequent runs: - // - First run: determine mode (lite vs standard) from VS Code setting - // written by the installer. - // Lite mode: applies chat-only layout; user clicks Setup to run /setup. - // Standard mode: injects /setup into Copilot Chat automatically. + // - First run: determine mode (maker vs developer) from VS Code setting + // written by the installer. Legacy values 'lite'/'standard' are + // normalized to 'maker'/'developer' in firstInstallDispatch. + // Maker mode: applies chat-only layout; user clicks Setup to run /setup. + // Developer mode: injects /setup into Copilot Chat automatically. // Empty ("") / "prompt": consolidated installer (ADO #7895603) left - // the choice to us — show a QuickPick, default to standard, then + // the choice to us - show a QuickPick, default to maker, then // route into the chosen branch. - // - Subsequent lite activations: silently re-apply layout. + // - Subsequent maker-mode activations: silently re-apply layout. const alreadyApplied = context.globalState.get(APPLIED_KEY, false); - const userWantsLite = context.globalState.get(LITE_MODE_KEY, true); // default to lite + const userWantsMakerLayout = context.globalState.get(LITE_MODE_KEY, true); // default to maker layout const installerMode = vscode.workspace.getConfiguration().get('essMaker.mode', ''); - _log(`activate: alreadyApplied=${alreadyApplied}, userWantsLite=${userWantsLite}, installerMode="${installerMode}", workspaceFolders=${vscode.workspace.workspaceFolders?.length || 0}`); + _log(`activate: alreadyApplied=${alreadyApplied}, userWantsMakerLayout=${userWantsMakerLayout}, installerMode="${installerMode}", workspaceFolders=${vscode.workspace.workspaceFolders?.length || 0}`); if (vscode.workspace.workspaceFolders?.length) { if (!alreadyApplied) { @@ -1595,11 +1613,11 @@ function activate(context) { // consolidated installer didn't pin it), then dispatch. firstInstallDispatch(context, installerMode) .catch(err => _log(`activate: firstInstallDispatch error: ${err && err.message}`)); - } else if (userWantsLite) { - // Subsequent lite mode launch: silently re-apply layout. + } else if (userWantsMakerLayout) { + // Subsequent maker-mode launch: silently re-apply layout. setTimeout(() => { applyChatOnlyLayout({ silent: true }).catch(() => {}); }, 1500); } - // If userWantsLite is false (standard mode), skip re-applying. + // If userWantsMakerLayout is false (developer mode), skip re-applying. // Auto-update nudge (ADO 7569528 / 7569530): check whether the local // clone is behind origin/main and, if so, offer a one-click pull. diff --git a/tools/ess-maker-profile/extension/extension.test.js b/tools/ess-maker-profile/extension/extension.test.js index 786dbb369..37f8bb342 100644 --- a/tools/ess-maker-profile/extension/extension.test.js +++ b/tools/ess-maker-profile/extension/extension.test.js @@ -206,13 +206,15 @@ test('extension source declares promptForInstallMode helper', () => { assert.ok(/async function promptForInstallMode\s*\(/.test(src), 'promptForInstallMode not declared'); }); -test('mode prompt offers Standard and Lite choices', () => { - assert.ok(/Standard \(recommended\)/.test(src), 'Standard option label missing'); - assert.ok(/Lite \(chat-first\)/.test(src), 'Lite option label missing'); +test('mode prompt offers Maker and Developer choices', () => { + assert.ok(/Maker \(recommended\)/.test(src), 'Maker option label missing'); + assert.ok(/label:\s*'Developer'/.test(src), 'Developer option label missing'); + assert.ok(/mode:\s*'maker'/.test(src), "mode value 'maker' missing"); + assert.ok(/mode:\s*'developer'/.test(src), "mode value 'developer' missing"); }); -test('mode prompt defaults to standard when the maker dismisses the QuickPick', () => { - assert.ok(/return pick \? pick\.mode : 'standard'/.test(src), 'Dismiss-defaults-to-standard fallback missing'); +test('mode prompt defaults to maker when the maker dismisses the QuickPick', () => { + assert.ok(/return pick \? pick\.mode : 'maker'/.test(src), 'Dismiss-defaults-to-maker fallback missing'); }); test('firstInstallDispatch prompts when installerMode is empty or "prompt"', () => { @@ -223,6 +225,22 @@ test('firstInstallDispatch persists the chosen mode to global settings', () => { assert.ok(/'essMaker\.mode',[\s\S]*?ConfigurationTarget\.Global/.test(src), 'chosen mode should be persisted with ConfigurationTarget.Global'); }); +test('legacy essMaker.mode values are normalized (lite -> maker, standard -> developer)', () => { + assert.ok(/function normalizeInstallerMode/.test(src), 'normalizeInstallerMode helper missing'); + assert.ok(/if\s*\(mode === 'lite'\)\s*return 'maker'/.test(src), "'lite' should be normalized to 'maker'"); + assert.ok(/if\s*\(mode === 'standard'\)\s*return 'developer'/.test(src), "'standard' should be normalized to 'developer'"); + assert.ok(/normalizeInstallerMode\(installerMode\)/.test(src), 'firstInstallDispatch should normalize before checking prompt'); +}); + +test('essMaker.mode config schema accepts the new maker/developer values', () => { + const modeProp = pkg.contributes.configuration.properties['essMaker.mode']; + assert.ok(modeProp, 'essMaker.mode missing from configuration'); + assert.ok(modeProp.enum.includes('maker'), "enum should include 'maker'"); + assert.ok(modeProp.enum.includes('developer'), "enum should include 'developer'"); + assert.ok(modeProp.enum.includes('lite'), "enum should still include legacy 'lite'"); + assert.ok(modeProp.enum.includes('standard'), "enum should still include legacy 'standard'"); +}); + console.log('\nauto-update: parseLsRemoteSha:'); test('extracts sha from a ls-remote line', () => { diff --git a/tools/ess-maker-profile/extension/package.json b/tools/ess-maker-profile/extension/package.json index 3b7f2aba9..2a9d75aeb 100644 --- a/tools/ess-maker-profile/extension/package.json +++ b/tools/ess-maker-profile/extension/package.json @@ -2,7 +2,7 @@ "name": "ess-maker-profile", "displayName": "ESS Maker", "description": "Chat-only, big-button experience for customizing your Employee Self-Service agent. POC.", - "version": "0.4.26", + "version": "0.4.27", "publisher": "microsoft-ess", "private": true, "engines": { @@ -49,7 +49,7 @@ }, { "command": "essMaker.restoreStandardLayout", - "title": "ESS Maker: Restore Standard Layout", + "title": "ESS Maker: Restore Developer Layout", "category": "ESS Maker" }, { @@ -92,9 +92,9 @@ "properties": { "essMaker.mode": { "type": "string", - "enum": ["lite", "standard", ""], + "enum": ["maker", "developer", "lite", "standard", ""], "default": "", - "description": "Install mode set by the ESS installer. 'lite' applies the chat-first layout; 'standard' uses the default VS Code layout with /setup injection; empty string means the consolidated installer left the choice unset and the extension will ask on first launch." + "description": "Install mode set by the ESS installer. 'maker' (was 'lite') applies the chat-first layout; 'developer' (was 'standard') uses the default VS Code layout with /setup injection; empty string means the consolidated installer left the choice unset and the extension will ask on first launch. The legacy 'lite'/'standard' values are still accepted and treated as 'maker'/'developer' so existing users keep their choice after the rename." }, "essMaker.autoUpdateCheck": { "type": "boolean", From ad3a06745163b2caadd1b94215dff4057fa2366b Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Wed, 23 Sep 2026 15:09:09 -0700 Subject: [PATCH 03/11] bootstrap: derive SourceBaseUrl from -Branch; stop pinning SkipMakerProfile Two bugs discovered during a fresh install test on the branch: 1. All three ps1 bootstraps hard-coded `SourceBaseUrl` to `.../main/setup`, so passing `-Branch ` still pulled `Install-EssAdk.ps1` from `main` - defeating the purpose of the `-Branch` param for testing feature branches. Fix: default `SourceBaseUrl` to `...//setup` when the caller does not pass an explicit override. 2. `bootstrap.ps1` was splatting `SkipMakerProfile = \True` when invoking `Install-EssAdk.ps1`. Pre-rename that pinned Standard mode; post-rename it pins Developer mode - either way it silently suppresses the first-launch QuickPick that this PR is centered on. Fix: drop the pin; forward a new `-InstallMode` param instead so callers can pin a mode explicitly when they want to, and everyone else lands on `prompt` (which fires the QuickPick). Also aligns bootstrap-lite.ps1 and bootstrap-dev.ps1 with the same Branch-derived SourceBaseUrl pattern. Refs: #296 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- setup/bootstrap-dev.ps1 | 8 +++++++- setup/bootstrap-lite.ps1 | 8 +++++++- setup/bootstrap.ps1 | 14 +++++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/setup/bootstrap-dev.ps1 b/setup/bootstrap-dev.ps1 index b0bf5c639..f884bab7c 100644 --- a/setup/bootstrap-dev.ps1 +++ b/setup/bootstrap-dev.ps1 @@ -33,12 +33,18 @@ param( [string] $InstallRoot, [string] $Branch = 'main', - [string] $SourceBaseUrl = 'https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup' + [string] $SourceBaseUrl ) $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 +# Derive SourceBaseUrl from -Branch when not explicitly set, so `-Branch ` +# actually pulls the installer bits from that feature branch (not from main). +if (-not $SourceBaseUrl) { + $SourceBaseUrl = "https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/$Branch/setup" +} + $tempDir = Join-Path $env:TEMP "ess-adk-bootstrap-$([Guid]::NewGuid().ToString('N').Substring(0,8))" New-Item -ItemType Directory -Path $tempDir -Force | Out-Null diff --git a/setup/bootstrap-lite.ps1 b/setup/bootstrap-lite.ps1 index 24a03961b..ef3cc23d9 100644 --- a/setup/bootstrap-lite.ps1 +++ b/setup/bootstrap-lite.ps1 @@ -35,12 +35,18 @@ param( [string] $InstallRoot, [string] $Branch = 'main', - [string] $SourceBaseUrl = 'https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup' + [string] $SourceBaseUrl ) $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 +# Derive SourceBaseUrl from -Branch when not explicitly set, so `-Branch ` +# actually pulls the installer bits from that feature branch (not from main). +if (-not $SourceBaseUrl) { + $SourceBaseUrl = "https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/$Branch/setup" +} + $tempDir = Join-Path $env:TEMP "ess-adk-bootstrap-$([Guid]::NewGuid().ToString('N').Substring(0,8))" New-Item -ItemType Directory -Path $tempDir -Force | Out-Null diff --git a/setup/bootstrap.ps1 b/setup/bootstrap.ps1 index 55b30136a..1a4190af8 100644 --- a/setup/bootstrap.ps1 +++ b/setup/bootstrap.ps1 @@ -26,12 +26,20 @@ param( [string] $InstallRoot, [string] $Branch = 'main', - [string] $SourceBaseUrl = 'https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup' + [ValidateSet('maker','developer','prompt','lite','standard','')] + [string] $InstallMode = '', + [string] $SourceBaseUrl ) $ErrorActionPreference = 'Stop' [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 +# Derive SourceBaseUrl from -Branch when not explicitly set, so `-Branch ` +# actually pulls the installer bits from that feature branch (not from main). +if (-not $SourceBaseUrl) { + $SourceBaseUrl = "https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/$Branch/setup" +} + $tempDir = Join-Path $env:TEMP "ess-adk-bootstrap-$([Guid]::NewGuid().ToString('N').Substring(0,8))" New-Item -ItemType Directory -Path $tempDir -Force | Out-Null @@ -76,6 +84,10 @@ $scriptContent = [System.IO.File]::ReadAllText($installer, [System.Text.Encoding $scriptBlock = [ScriptBlock]::Create($scriptContent) $installerArgs = @{ Branch = $Branch } +# Forward -InstallMode when the caller pinned one; otherwise leave the +# installer to fall back to its own default (prompt), which fires the +# in-VS-Code Maker/Developer QuickPick on first launch. +if ($InstallMode) { $installerArgs.InstallMode = $InstallMode } if ($InstallRoot) { $installerArgs.InstallRoot = $InstallRoot } & $scriptBlock @installerArgs From fb500e2854c218ff7f5754c78ef2e1b63de56b6c Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Wed, 23 Sep 2026 15:20:03 -0700 Subject: [PATCH 04/11] ess-maker-profile: rebuild vsix at 0.4.27 (Maker/Developer rename) The Windows installer installs the extension by globbing ess-maker-profile-*.vsix in the cloned repo, so the source-side rename in this PR never reaches installed extensions unless the checked-in vsix is rebuilt. Replaces the stale 0.4.25 vsix with a fresh 0.4.27 build (npx @vscode/vsce package). Refs: #296 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- .../extension/ess-maker-profile-0.4.25.vsix | Bin 30666 -> 0 bytes .../extension/ess-maker-profile-0.4.27.vsix | Bin 0 -> 32923 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tools/ess-maker-profile/extension/ess-maker-profile-0.4.25.vsix create mode 100644 tools/ess-maker-profile/extension/ess-maker-profile-0.4.27.vsix diff --git a/tools/ess-maker-profile/extension/ess-maker-profile-0.4.25.vsix b/tools/ess-maker-profile/extension/ess-maker-profile-0.4.25.vsix deleted file mode 100644 index 8cf62342d914bba824d7681992d4277408545b33..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 30666 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 z zw{x%SStIl{t@T;=rb_#U;5xxG5=LN?Mq@cHu#7v3dFUV7z5hvd66h2*k(XH=wL=ry ziL>*o5DenED)HWI1&ZX>TGitT>SmxCRb;CU)B;Pqx+%xwmzzV)k|oJVbrQ)hrvo&g z>ZHnhs|c8g)E;>A@Sm+Ny195_g9*FNet5_xhm&Wtxb+Z!j=F6=zKj70g;fe`r}SkU zw*Q~TaWXQn{W1<+T>W3#$fA!x&!M2)XpA%7g2Mo-Qs)!O@gzA7a_|W0(;kL>V_!Gk zt*#yHK*Dl=D;t&R(%Ta=A~+Gq0l)gTN8Hio-EC?qSZ>X1 zCS69x=^BR=@Lc6KtV9*0IbXfVk^_AdiP9x^eS0TnJ5FYuOe79Z`5b@R`mHvhXE0ut zIg=P|$HuafHLPiqeFQQz;nAG0(LXIdi~!)#b{!>iyy|+?f|Qu&ow;&9zC<-3p(-X1 zP;bA8>+-*XGhh$_(0><4|9ad18cqIx9sWCj{vT553Nrr_<3ClY|2F~|HSz3)$88?fBlfZ7BT+}r3wH3^w$yK zf9mj0fdBiF=0DN?8ASaRe*ZlH{=QECiuON)@&APU=T-Et=m$| YHVQJ}5Py9$+}FeMi^e!Uf4%yD0BOYrkpKVy literal 0 HcmV?d00001 From 2a7e21becfbfb48fde6409126294afb8a18fe925 Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Thu, 24 Sep 2026 09:26:19 -0700 Subject: [PATCH 05/11] installer: prompt Maker/Developer in the CLI instead of a VS Code QuickPick The 0.4.27 first-launch Maker/Developer QuickPick approach reliably lost the race against the theme picker and Copilot sign-in on VS Code's first launch, so makers never actually saw it - they were silently landed in Maker mode regardless of intent. Reproduced twice on a fresh Windows install with all VS Code state wiped. Move the question into the installer CLI, before we hand off to VS Code, so the answer is deterministic and applied before any editor UI appears: - setup/Install-EssAdk.ps1: when \ -eq 'prompt' (the default when the base bootstrap.ps1 is used), prompt in the terminal with a numbered Maker/Developer menu. Skip the prompt under CI / TF_BUILD / GITHUB_ACTIONS / redirected stdin and default to Maker with a visible notice, so scripted callers keep working. - setup/install-ess-adk.sh: mirror on macOS. Read from /dev/tty when available so the prompt still works under the standard `curl ... | bash` bootstrap where stdin is the script body, not the terminal. - tools/ess-maker-profile/extension/extension.js: remove promptForInstallMode and the in-VS-Code QuickPick. firstInstallDispatch now trusts the value the installer wrote to essMaker.mode; a blank / literal 'prompt' value silently falls back to Maker (no first-launch modal), which preserves deterministic behavior for stray VS Code windows that never went through the installer. - Extension bumped to 0.4.28; vsix rebuilt so fresh installs actually pick up the change (the checked-in vsix is what Install-EssAdk.ps1 globs; source-only edits don't reach installed extensions). - Tests updated: PS 62/62 (added prompt-copy + non-interactive assertions for both Install-EssAdk.ps1 and install-ess-adk.sh); extension 46/46 (replaced QuickPick assertions with 'QuickPick is gone' + 'blank/prompt defaults to maker without a modal'). Refs: #296 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- setup/Install-EssAdk.Tests.ps1 | 42 +++++++++++++ setup/Install-EssAdk.ps1 | 43 ++++++++++++++ setup/install-ess-adk.sh | 50 ++++++++++++++++ .../ess-maker-profile/extension/CHANGELOG.md | 16 +++++ .../extension/ess-maker-profile-0.4.27.vsix | Bin 32923 -> 0 bytes .../extension/ess-maker-profile-0.4.28.vsix | Bin 0 -> 33165 bytes .../ess-maker-profile/extension/extension.js | 55 ++++++------------ .../extension/extension.test.js | 33 +++++------ .../ess-maker-profile/extension/package.json | 2 +- 9 files changed, 185 insertions(+), 56 deletions(-) delete mode 100644 tools/ess-maker-profile/extension/ess-maker-profile-0.4.27.vsix create mode 100644 tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix diff --git a/setup/Install-EssAdk.Tests.ps1 b/setup/Install-EssAdk.Tests.ps1 index 78d8f0b35..227e00fd2 100644 --- a/setup/Install-EssAdk.Tests.ps1 +++ b/setup/Install-EssAdk.Tests.ps1 @@ -70,6 +70,30 @@ Test 'legacy InstallMode values (lite/standard) are coerced to maker/developer' } } +Test 'InstallMode=prompt fires a terminal Maker/Developer prompt (not a VS Code QuickPick)' { + # Historically we tried to ask the mode question from inside VS Code + # via a QuickPick, but that surface competes with the theme picker and + # Copilot sign-in on first launch and reliably loses the race. The + # installer now asks the question in the terminal before handing off + # to VS Code so the answer is deterministic. + if ($src -notmatch "if\s*\(\`$InstallMode\s+-eq\s+'prompt'\)") { + throw 'no prompt branch guarding InstallMode=prompt' + } + if ($src -notmatch 'Read-Host') { throw 'terminal prompt must use Read-Host' } + if ($src -notmatch 'Maker \(recommended\)') { throw 'Maker option label missing from prompt copy' } + if ($src -notmatch '\[2\] Developer') { throw 'Developer option label missing from prompt copy' } +} + +Test 'InstallMode=prompt defaults to maker under non-interactive stdin / CI' { + if ($src -notmatch 'IsInputRedirected') { + throw 'non-interactive detection (Console::IsInputRedirected) missing' + } + if ($src -notmatch '\$env:CI') { throw 'CI env-var non-interactive guard missing' } + if ($src -notmatch 'Defaulting to Maker mode') { + throw 'non-interactive branch should default to Maker mode with a visible message' + } +} + Test 'script declares SkipLaunch parameter' { if ($src -notmatch '\[switch\]\s*\$SkipLaunch') { throw 'SkipLaunch switch parameter not found' @@ -396,6 +420,24 @@ Test 'install-ess-adk.sh honors INSTALL_MODE (maker|developer|prompt) with legac if ($macInstaller -notmatch 'INSTALL_MODE"?\s*==\s*"maker"') { throw 'maker launch branch missing' } } +Test 'install-ess-adk.sh INSTALL_MODE=prompt fires a terminal Maker/Developer prompt' { + if ($macInstaller -notmatch 'INSTALL_MODE"?\s*==\s*"prompt"') { throw 'no prompt branch guarding INSTALL_MODE=prompt' } + if ($macInstaller -notmatch 'read -r answer') { throw 'terminal read for maker/developer answer missing' } + if ($macInstaller -notmatch '/dev/tty') { + throw '/dev/tty read fallback required so the prompt works when the script is piped through bash from a curl one-liner' + } + if ($macInstaller -notmatch 'Maker \(recommended\)') { throw 'Maker option label missing from prompt copy' } + if ($macInstaller -notmatch '\[2\] Developer') { throw 'Developer option label missing from prompt copy' } +} + +Test 'install-ess-adk.sh INSTALL_MODE=prompt defaults to maker under non-interactive / CI' { + if ($macInstaller -notmatch '\$\{CI:-\}') { throw 'CI env-var non-interactive guard missing' } + if ($macInstaller -notmatch '! -t 0') { throw 'stdin-is-a-tty non-interactive guard missing' } + if ($macInstaller -notmatch 'Defaulting to Maker mode') { + throw 'non-interactive branch should default to Maker mode with a visible message' + } +} + Test 'bootstrap-dev-mac.sh exists and pins INSTALL_MODE=developer' { $devMacPath = Join-Path $PSScriptRoot 'bootstrap-dev-mac.sh' if (-not (Test-Path $devMacPath)) { throw 'bootstrap-dev-mac.sh missing' } diff --git a/setup/Install-EssAdk.ps1 b/setup/Install-EssAdk.ps1 index fcde2e5b2..ec23bb6ee 100644 --- a/setup/Install-EssAdk.ps1 +++ b/setup/Install-EssAdk.ps1 @@ -110,6 +110,49 @@ if ($SkipMakerProfile) { $InstallMode = 'developer' } if ($InstallMode -eq 'lite') { $InstallMode = 'maker' } if ($InstallMode -eq 'standard') { $InstallMode = 'developer' } +# When the caller didn't pin a mode (the default one-liner path via +# bootstrap.ps1), prompt the maker in the terminal for their preference. +# Historically we tried to prompt from inside VS Code via a QuickPick, +# but that surface competes with the theme picker + GitHub Copilot +# sign-in on first launch and reliably loses the race - the maker never +# sees it. Prompting here in the CLI, before we hand off to VS Code, +# means the choice is applied deterministically before any editor UI +# appears. +if ($InstallMode -eq 'prompt') { + $nonInteractive = $env:CI -or $env:TF_BUILD -or $env:GITHUB_ACTIONS -or [Console]::IsInputRedirected + if ($nonInteractive) { + Write-Host "" + Write-Host "Non-interactive environment detected. Defaulting to Maker mode." -ForegroundColor Yellow + $InstallMode = 'maker' + } else { + Write-Host "" + Write-Host "==> Choose your ESS Maker experience" -ForegroundColor Cyan + Write-Host " [1] Maker (recommended)" + Write-Host " Chat-first layout; hides file tree, tabs, and status bar;" + Write-Host " big-button Quick Actions rail. Best if you mostly work in" + Write-Host " chat and want a focused HR/IT admin surface." + Write-Host "" + Write-Host " [2] Developer" + Write-Host " Default VS Code layout with GitHub Copilot Chat in the side" + Write-Host " panel and /setup injected automatically. Best if you plan" + Write-Host " to inspect or edit files directly." + Write-Host "" + $choice = $null + while ($null -eq $choice) { + $answer = Read-Host "Enter 1 for Maker, 2 for Developer (default: 1)" + $answer = ($answer ?? '').Trim() + switch -Regex ($answer) { + '^(1|maker|m|)$' { $choice = 'maker' } + '^(2|developer|dev|d)$' { $choice = 'developer' } + default { Write-Host "Please enter 1 or 2." -ForegroundColor Yellow } + } + } + $InstallMode = $choice + Write-Host " Selected: $InstallMode" -ForegroundColor Green + Write-Host "" + } +} + # Canonical mode label used throughout this script for both telemetry and # the VS Code settings write. Kept in $modeLabel so all downstream # references (5c extension install, launch, telemetry) share one source diff --git a/setup/install-ess-adk.sh b/setup/install-ess-adk.sh index ce0be8135..a99e4d9d0 100644 --- a/setup/install-ess-adk.sh +++ b/setup/install-ess-adk.sh @@ -47,6 +47,56 @@ case "$INSTALL_MODE" in INSTALL_MODE="prompt" ;; esac + +# When the caller didn't pin a mode (the default one-liner path via +# bootstrap-mac.sh), prompt the maker in the terminal for their preference. +# Historically we tried to prompt from inside VS Code via a QuickPick, but +# that surface competes with the theme picker + GitHub Copilot sign-in on +# first launch and reliably loses the race - the maker never sees it. +# Prompting here in the CLI, before we hand off to VS Code, means the +# choice is applied deterministically before any editor UI appears. +if [[ "$INSTALL_MODE" == "prompt" ]]; then + if [[ -n "${CI:-}" || -n "${TF_BUILD:-}" || -n "${GITHUB_ACTIONS:-}" ]] || [[ ! -t 0 ]]; then + echo "" + echo "Non-interactive environment detected. Defaulting to Maker mode." + INSTALL_MODE="maker" + else + echo "" + echo "==> Choose your ESS Maker experience" + echo " [1] Maker (recommended)" + echo " Chat-first layout; hides file tree, tabs, and status bar;" + echo " big-button Quick Actions rail. Best if you mostly work in" + echo " chat and want a focused HR/IT admin surface." + echo "" + echo " [2] Developer" + echo " Default VS Code layout with GitHub Copilot Chat in the side" + echo " panel and /setup injected automatically. Best if you plan" + echo " to inspect or edit files directly." + echo "" + while true; do + printf "Enter 1 for Maker, 2 for Developer (default: 1): " + # Read from the terminal directly so this works even when the + # bootstrap piped install-ess-adk.sh through bash (stdin is the + # script, not the tty). + if [[ -r /dev/tty ]]; then + read -r answer Maker (default), Standard -> Developer.** diff --git a/tools/ess-maker-profile/extension/ess-maker-profile-0.4.27.vsix b/tools/ess-maker-profile/extension/ess-maker-profile-0.4.27.vsix deleted file mode 100644 index 9c6130bfaa9854e63678cbc8fd48071d5da6bf0a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32923 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 z zw{x%SStIl{t@T;=rb_#U;5xxG5=LN?Mq@cHu#7v3dFUV7z5hvd66h2*k(XH=wL=ry ziL>*o5DenED)HWI1&ZX>TGitT>SmxCRb;CU)B;Pqx+%xwmzzV)k|oJVbrQ)hrvo&g z>ZHnhs|c8g)E;>A@Sm+Ny195_g9*FNet5_xhm&Wtxb+Z!j=F6=zKj70g;fe`r}SkU zw*Q~TaWXQn{W1<+T>W3#$fA!x&!M2)XpA%7g2Mo-Qs)!O@gzA7a_|W0(;kL>V_!Gk zt*#yHK*Dl=D;t&R(%Ta=A~+Gq0l)gTN8Hio-EC?qSZ>X1 zCS69x=^BR=@Lc6KtV9*0IbXfVk^_AdiP9x^eS0TnJ5FYuOe79Z`5b@R`mHvhXE0ut zIg=P|$HuafHLPiqeFQQz;nAG0(LXIdi~!)#b{!>iyy|+?f|Qu&ow;&9zC<-3p(-X1 zP;bA8>+-*XGhh$_(0><4|9ad18cqIx9sWCj{vT553Nrr_<3ClY|2F~|HSz3)$88?fBlfZ7BT+}r3wH3^w$yK zf9mj0fdBiF=0DN?8ASaRe*ZlH{=QECiuON)@&APU=T-Et=m$| YHVQJ}5Py9$+}FeMi^e!Uf4%yD0BOYrkpKVy diff --git a/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix b/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix new file mode 100644 index 0000000000000000000000000000000000000000..6932f095bf3e77afac51154dd4fd2f39b90171b6 GIT binary patch literal 33165 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 OewcmJ7{=?bcmD?|CwKY) literal 0 HcmV?d00001 diff --git a/tools/ess-maker-profile/extension/extension.js b/tools/ess-maker-profile/extension/extension.js index 155004c78..1b0716c83 100644 --- a/tools/ess-maker-profile/extension/extension.js +++ b/tools/ess-maker-profile/extension/extension.js @@ -1430,45 +1430,24 @@ async function maybePromptReinstall(repoRoot) { } // --- First-install dispatch (ADO #7895603 consolidated installer) --------- -// When the consolidated installer leaves essMaker.mode unset ("" or -// "prompt"), we prompt the maker on their first VS Code launch to pick -// maker (chat-first) vs developer (default VS Code view) and persist the -// choice to the essMaker.mode global setting so subsequent launches skip -// the prompt. This exists so the consolidated Windows installer doesn't -// need to interrupt the CLI with a mode question - the choice surfaces in -// VS Code where the maker can preview the two options as they read them. +// The consolidated installer (setup/Install-EssAdk.ps1 / install-ess-adk.sh) +// prompts the maker for maker vs developer in the terminal BEFORE VS Code +// launches, and writes the resolved mode to essMaker.mode in settings.json. +// So by the time this extension activates, essMaker.mode is always one of +// 'maker' | 'developer' | 'lite' (legacy) | 'standard' (legacy). +// +// If the installer left the value blank ('' or 'prompt') - e.g. a maker +// double-clicked the extension into a stray VS Code window without +// running the installer, or an older non-interactive install flow slipped +// through - we default to 'maker' silently rather than pop a modal on +// first launch. First-launch modals reliably lose the race against the +// theme picker and Copilot sign-in prompts and are never seen by makers. +// The mode can always be changed later via the Quick Actions toggle or +// the essMaker.mode setting. // // Legacy value migration: the pre-rename installer wrote 'lite'/'standard' // to essMaker.mode. Reads here normalize those to the new 'maker'/'developer' // values so existing users keep the mode they picked. -async function promptForInstallMode() { - const picks = [ - { - label: 'Maker (recommended)', - description: 'Hides file tree, tabs, and status bar; big-button Quick Actions rail', - detail: 'Best if you mostly work in chat and want a focused HR/IT admin surface.', - mode: 'maker', - }, - { - label: 'Developer', - description: 'Default VS Code layout with GitHub Copilot Chat in the side panel', - detail: 'Best if you plan to inspect or edit files directly. /setup runs automatically.', - mode: 'developer', - }, - ]; - try { - const pick = await vscode.window.showQuickPick(picks, { - placeHolder: 'Choose your ESS Maker experience (you can change this later from the Command Palette)', - ignoreFocusOut: true, - matchOnDescription: true, - matchOnDetail: true, - }); - return pick ? pick.mode : 'maker'; - } catch (err) { - _log(`promptForInstallMode: error ${err && err.message}`); - return 'maker'; - } -} // Normalize legacy essMaker.mode values ('lite', 'standard') written by // the pre-rename installer to the new canonical names, so existing users @@ -1482,7 +1461,11 @@ function normalizeInstallerMode(mode) { async function firstInstallDispatch(context, installerMode) { let effectiveMode = normalizeInstallerMode(installerMode); if (!effectiveMode || effectiveMode === 'prompt') { - effectiveMode = await promptForInstallMode(); + // Installer didn't resolve a mode (blank or literal 'prompt'). Fall + // back to maker silently and persist so we don't fall through here + // on every activation. + _log(`firstInstallDispatch: installer mode was "${installerMode}"; defaulting to maker`); + effectiveMode = 'maker'; try { await vscode.workspace.getConfiguration().update( 'essMaker.mode', diff --git a/tools/ess-maker-profile/extension/extension.test.js b/tools/ess-maker-profile/extension/extension.test.js index 37f8bb342..3ebdb800f 100644 --- a/tools/ess-maker-profile/extension/extension.test.js +++ b/tools/ess-maker-profile/extension/extension.test.js @@ -200,36 +200,31 @@ test('exposes the essMaker.autoUpdateCheck opt-out setting', () => { assert.strictEqual(prop.default, true); }); -console.log('\nfirst-install mode prompt (ADO #7895603):'); +console.log('\nfirst-install mode dispatch (ADO #7895603):'); -test('extension source declares promptForInstallMode helper', () => { - assert.ok(/async function promptForInstallMode\s*\(/.test(src), 'promptForInstallMode not declared'); +test('extension source does NOT declare an in-VS-Code mode QuickPick', () => { + // The mode is now resolved in the installer CLI before VS Code launches + // (setup/Install-EssAdk.ps1 + install-ess-adk.sh prompt there). A VS Code + // QuickPick on first launch reliably loses the race against the theme + // picker + Copilot sign-in, so it was removed. + assert.ok(!/async function promptForInstallMode/.test(src), 'promptForInstallMode should be gone; the installer prompts in the CLI'); + assert.ok(!/showQuickPick\([\s\S]{0,200}?Maker \(recommended\)/.test(src), 'in-VS-Code Maker/Developer QuickPick should be removed'); }); -test('mode prompt offers Maker and Developer choices', () => { - assert.ok(/Maker \(recommended\)/.test(src), 'Maker option label missing'); - assert.ok(/label:\s*'Developer'/.test(src), 'Developer option label missing'); - assert.ok(/mode:\s*'maker'/.test(src), "mode value 'maker' missing"); - assert.ok(/mode:\s*'developer'/.test(src), "mode value 'developer' missing"); +test('firstInstallDispatch defaults blank / "prompt" installer values to maker', () => { + assert.ok(/if\s*\(!effectiveMode \|\| effectiveMode === 'prompt'\)/.test(src), 'fallback branch should still guard blank / prompt'); + assert.ok(/effectiveMode = 'maker'/.test(src), 'fallback should default to maker without a modal'); }); -test('mode prompt defaults to maker when the maker dismisses the QuickPick', () => { - assert.ok(/return pick \? pick\.mode : 'maker'/.test(src), 'Dismiss-defaults-to-maker fallback missing'); -}); - -test('firstInstallDispatch prompts when installerMode is empty or "prompt"', () => { - assert.ok(/if\s*\(!effectiveMode \|\| effectiveMode === 'prompt'\)/.test(src), 'promptForInstallMode should be invoked when installer setting is unset'); -}); - -test('firstInstallDispatch persists the chosen mode to global settings', () => { - assert.ok(/'essMaker\.mode',[\s\S]*?ConfigurationTarget\.Global/.test(src), 'chosen mode should be persisted with ConfigurationTarget.Global'); +test('firstInstallDispatch persists the resolved mode to global settings', () => { + assert.ok(/'essMaker\.mode',[\s\S]*?ConfigurationTarget\.Global/.test(src), 'resolved mode should be persisted with ConfigurationTarget.Global'); }); test('legacy essMaker.mode values are normalized (lite -> maker, standard -> developer)', () => { assert.ok(/function normalizeInstallerMode/.test(src), 'normalizeInstallerMode helper missing'); assert.ok(/if\s*\(mode === 'lite'\)\s*return 'maker'/.test(src), "'lite' should be normalized to 'maker'"); assert.ok(/if\s*\(mode === 'standard'\)\s*return 'developer'/.test(src), "'standard' should be normalized to 'developer'"); - assert.ok(/normalizeInstallerMode\(installerMode\)/.test(src), 'firstInstallDispatch should normalize before checking prompt'); + assert.ok(/normalizeInstallerMode\(installerMode\)/.test(src), 'firstInstallDispatch should normalize before checking fallback'); }); test('essMaker.mode config schema accepts the new maker/developer values', () => { diff --git a/tools/ess-maker-profile/extension/package.json b/tools/ess-maker-profile/extension/package.json index 2a9d75aeb..9f9f6b745 100644 --- a/tools/ess-maker-profile/extension/package.json +++ b/tools/ess-maker-profile/extension/package.json @@ -2,7 +2,7 @@ "name": "ess-maker-profile", "displayName": "ESS Maker", "description": "Chat-only, big-button experience for customizing your Employee Self-Service agent. POC.", - "version": "0.4.27", + "version": "0.4.28", "publisher": "microsoft-ess", "private": true, "engines": { From f0228ef29135b33c03656c743ed1be0a71514b4b Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Thu, 24 Sep 2026 11:22:37 -0700 Subject: [PATCH 06/11] installer: prune stale QuickPick/'first launch' copy after CLI-prompt move The mode prompt now lives in the installer terminal, but the surrounding copy and code still referenced the removed VS Code QuickPick and 'first launch' modal: - Install-EssAdk.ps1: developer option copy dropped `/setup injected automatically` line; -InstallMode docstring, extension-install comments, launch-block comments, and the dead `prompt` -> `''` branch in the settings.json write are all removed. The launch block now has just two branches (developer / maker) since the CLI prompt resolves `prompt` to a concrete mode before we reach any launch code. - install-ess-adk.sh: mirror cleanup - developer option copy, INSTALL_MODE comment, launch-block comment, and the dead `prompt` -> `''` branch in the settings.json write. - bootstrap.ps1 / bootstrap-dev.ps1 / bootstrap-lite.ps1: docstring references to the `in-VS-Code Maker/Developer QuickPick on first launch` are corrected to point at the terminal prompt. - extension.js: firstInstallDispatch activate-comment updated to note the silent default-to-maker (no more first-launch modal) and that the QuickPick was removed because it lost the race against the theme picker and Copilot sign-in. - package.json: essMaker.mode description corrected - empty string now means the installer left the choice unresolved and the extension silently defaults to maker (not 'will ask on first launch'). - setup/README.md: 'on first VS Code launch' -> 'in the terminal before VS Code launches' in the flow copy, Maker/Developer heading, the bootstrap.ps1 / bootstrap-mac.sh file-table rows, and the macOS install snippet. - telemetry/install-telemetry.ps1: InstallMode docstring corrected. - Install-EssAdk.Tests.ps1: replaced the 'prompt -> empty' setting-write assertion with the new '\ = \' contract; relaxed the macOS launch-branch assertion (maker is now the fall-through else, not an explicit == check). Rebuilt ess-maker-profile-0.4.28.vsix so the shipped bits include the updated essMaker.mode description and firstInstallDispatch comment. Tests: PS 62/62 + extension 46/46 pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- setup/Install-EssAdk.Tests.ps1 | 13 +++-- setup/Install-EssAdk.ps1 | 41 ++++++---------- setup/README.md | 12 ++--- setup/bootstrap-dev.ps1 | 8 +-- setup/bootstrap-lite.ps1 | 6 +-- setup/bootstrap.ps1 | 4 +- setup/install-ess-adk.sh | 46 ++++++------------ setup/telemetry/install-telemetry.ps1 | 4 +- .../extension/ess-maker-profile-0.4.28.vsix | Bin 33165 -> 33209 bytes .../ess-maker-profile/extension/extension.js | 13 +++-- .../ess-maker-profile/extension/package.json | 2 +- 11 files changed, 63 insertions(+), 86 deletions(-) diff --git a/setup/Install-EssAdk.Tests.ps1 b/setup/Install-EssAdk.Tests.ps1 index 227e00fd2..742ddae0e 100644 --- a/setup/Install-EssAdk.Tests.ps1 +++ b/setup/Install-EssAdk.Tests.ps1 @@ -128,12 +128,12 @@ Test 'non-git directory detection exists' { } } -Test 'extension installs in every mode (writes essMaker.mode setting; prompt -> empty)' { +Test 'extension installs in every mode (writes essMaker.mode setting from resolved $modeLabel)' { if ($src -notmatch "essMaker\.mode.*\`$settingsModeValue") { throw 'Mode setting write logic (uses $settingsModeValue) not found' } - if ($src -notmatch "if\s*\(\`$modeLabel\s+-eq\s+'prompt'\)\s*\{\s*''") { - throw "prompt mode should map to '' in essMaker.mode (extension prompts on first launch)" + if ($src -notmatch '\$settingsModeValue\s*=\s*\$modeLabel') { + throw '$settingsModeValue should be assigned directly from the CLI-resolved $modeLabel (never "prompt" by this point)' } } @@ -149,7 +149,7 @@ Test 'bootstrap.ps1 parses without errors' { if ($errors.Count -gt 0) { throw "Parse errors: $($errors[0].Message)" } } -Test 'bootstrap.ps1 does not pin SkipMakerProfile (consolidated installer prompts in VS Code)' { +Test 'bootstrap.ps1 does not pin SkipMakerProfile (consolidated installer resolves mode in the CLI)' { if ($bootstrapSrc -match 'SkipMakerProfile\s*=\s*\$true') { throw 'bootstrap.ps1 should not set SkipMakerProfile = $true after installer consolidation (ADO #7895603)' } @@ -417,7 +417,10 @@ Test 'install-ess-adk.sh honors INSTALL_MODE (maker|developer|prompt) with legac throw "legacy 'standard' should be coerced to 'developer' on macOS" } if ($macInstaller -notmatch 'INSTALL_MODE"?\s*==\s*"developer"') { throw 'developer launch branch missing' } - if ($macInstaller -notmatch 'INSTALL_MODE"?\s*==\s*"maker"') { throw 'maker launch branch missing' } + # Maker is now the fall-through `else` branch of the launch block (prompt + # is resolved to maker|developer before any launch code runs), so we + # assert the maker log copy is present instead of the explicit == check. + if ($macInstaller -notmatch 'ESS Maker Profile will run /setup') { throw 'maker launch branch (fall-through else) missing' } } Test 'install-ess-adk.sh INSTALL_MODE=prompt fires a terminal Maker/Developer prompt' { diff --git a/setup/Install-EssAdk.ps1 b/setup/Install-EssAdk.ps1 index ec23bb6ee..bcef30198 100644 --- a/setup/Install-EssAdk.ps1 +++ b/setup/Install-EssAdk.ps1 @@ -56,9 +56,9 @@ .PARAMETER InstallMode Selects the VS Code experience: 'maker' (chat-first, hidden developer chrome - was 'lite'), 'developer' (default VS Code layout with /setup - injection - was 'standard'), or 'prompt' (default: let the ESS Maker - Profile extension ask the maker inside VS Code on first launch, default - to 'maker' if dismissed). The ESS Maker Profile extension is installed + injection - was 'standard'), or 'prompt' (default: this installer + asks the maker in the terminal, defaulting to 'maker' under a + non-interactive shell). The ESS Maker Profile extension is installed in every mode; only the layout and /setup delivery differ. Explicit values are respected without a prompt; 'prompt' is the recommended default for new customers. The legacy values 'lite' and 'standard' are @@ -133,9 +133,9 @@ if ($InstallMode -eq 'prompt') { Write-Host " chat and want a focused HR/IT admin surface." Write-Host "" Write-Host " [2] Developer" - Write-Host " Default VS Code layout with GitHub Copilot Chat in the side" - Write-Host " panel and /setup injected automatically. Best if you plan" - Write-Host " to inspect or edit files directly." + Write-Host " Default VS Code layout with GitHub Copilot Chat in the" + Write-Host " side panel. Best if you plan to inspect or edit files" + Write-Host " directly." Write-Host "" $choice = $null while ($null -eq $choice) { @@ -1039,9 +1039,9 @@ if (-not $FlightCheckOnly -and -not $SkipExtensions) { # Install the ESS Maker Profile extension in every mode. In maker mode # it applies the chat-first layout; in developer mode it only handles # /setup injection after the welcome wizard closes (no visual - # changes); in prompt mode it asks the maker inside VS Code on first - # launch and applies whichever mode they pick. $modeLabel is set in - # the param block above from -InstallMode / -SkipMakerProfile. + # changes). $modeLabel is always 'maker' or 'developer' by this + # point - the CLI prompt above resolves 'prompt' before we reach any + # of the install steps. Write-Step "Installing ESS Maker Profile ($modeLabel mode)" $code = Resolve-CodeCommand @@ -1096,14 +1096,12 @@ if (-not $FlightCheckOnly -and -not $SkipExtensions) { } # Write the mode setting so the extension knows whether to apply - # the maker (chat-first) layout, inject /setup (developer mode), - # or prompt the maker inside VS Code on first launch (prompt mode - # -> empty string, which the extension treats as "not decided"). + # the maker (chat-first) layout or inject /setup (developer mode). # Uses string manipulation to preserve JSONC comments in settings.json. $settingsDir = Join-Path $env:APPDATA 'Code\User' if (-not (Test-Path $settingsDir)) { New-Item -ItemType Directory -Path $settingsDir -Force | Out-Null } $settingsFile = Join-Path $settingsDir 'settings.json' - $settingsModeValue = if ($modeLabel -eq 'prompt') { '' } else { $modeLabel } + $settingsModeValue = $modeLabel $modeEntry = "`"essMaker.mode`": `"$settingsModeValue`"" if (Test-Path $settingsFile) { $raw = Get-Content $settingsFile -Raw @@ -1485,10 +1483,9 @@ if (-not $SkipLaunch) { # handles layout + /setup injection after the welcome wizard closes. # - Developer mode: use `code chat '/setup'` which opens Copilot Chat in # the sidebar panel on the right (the standard chat experience). - # - Prompt mode: open the workspace. The ESS Maker Profile extension - # asks the maker which experience they want on first launch, then - # applies the chosen mode's flow (layout+injection for maker, no - # layout change + chat open for developer). + # By the time we get here $modeLabel is always 'maker' or 'developer' + # (the CLI prompt above resolves 'prompt' before we reach any launch + # code), so there is no third fall-through branch to handle. Push-Location $workspace try { if ($modeLabel -eq 'developer') { @@ -1508,21 +1505,13 @@ if (-not $SkipLaunch) { Write-Host "If VS Code prompts you to trust the workspace or sign in to GitHub/Copilot, accept those prompts and /setup will run." -ForegroundColor Yellow Write-Host "If /setup does not start after trust/sign-in, open Copilot Chat manually and run /setup." -ForegroundColor Yellow } - } elseif ($modeLabel -eq 'maker') { + } else { # Maker mode - extension handles /setup after welcome wizard Write-Step 'Opening workspace in VS Code' Start-Process -FilePath $codePath -ArgumentList @('.') | Out-Null Write-Ok "Launched VS Code at $workspace" Write-Host "The ESS Maker Profile will run /setup in Copilot Chat after the welcome screen closes." -ForegroundColor Yellow Write-Host "If VS Code prompts you to trust the workspace, accept the prompt." -ForegroundColor Yellow - } else { - # Prompt mode - extension will ask which experience to apply - # on first launch, then run the appropriate flow. - Write-Step 'Opening workspace in VS Code' - Start-Process -FilePath $codePath -ArgumentList @('.') | Out-Null - Write-Ok "Launched VS Code at $workspace" - Write-Host "VS Code will ask you to pick a chat-first (maker) or default developer experience on first launch." -ForegroundColor Yellow - Write-Host "If VS Code prompts you to trust the workspace, accept the prompt." -ForegroundColor Yellow } } finally { Pop-Location } } else { diff --git a/setup/README.md b/setup/README.md index 94770b28f..8141ab9e6 100644 --- a/setup/README.md +++ b/setup/README.md @@ -14,15 +14,15 @@ iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-mac.sh)" ``` -Once complete, VS Code opens at `solutions/ess-maker-skills/` and - on the first launch - you'll be asked to pick **Maker** (chat-first, big-button layout - recommended) or **Developer** (default VS Code view with Copilot Chat in the side panel). Maker is the recommended default and will be applied if you dismiss the prompt. `/setup` is then automatically requested in Copilot Chat. You'll be prompted to trust the workspace and sign in to GitHub/Copilot - accept these prompts and `/setup` will connect the workspace to an existing editable DA Dev agent. +Once complete, the installer asks in the terminal which experience you want — **Maker** (chat-first, big-button layout — recommended) or **Developer** (default VS Code view with Copilot Chat in the side panel). Under a non-interactive shell (CI, piped input) the installer silently picks Maker. VS Code then opens at `solutions/ess-maker-skills/` and `/setup` is automatically requested in Copilot Chat. You'll be prompted to trust the workspace and sign in to GitHub/Copilot — accept these prompts and `/setup` will connect the workspace to an existing editable DA Dev agent. > **GitHub Copilot subscription is required** for the in-editor maker experience. This script installs the toolchain and extension scaffolding; it does not grant the Copilot entitlement. ## Maker Mode (Chat-First Layout) and Developer Mode -The one-shot installer (`bootstrap.ps1` / `bootstrap-mac.sh`) asks you to choose between **Maker** and **Developer** on first VS Code launch, so mode selection is a one-click choice from the main installer - no separate command needed. Maker was previously called "Lite" and Developer was previously called "Standard"; the old names still work for pinned scripts and previously-installed users. +The one-shot installer (`bootstrap.ps1` / `bootstrap-mac.sh`) asks you to choose between **Maker** and **Developer** in the terminal before VS Code launches, so mode selection is a one-line choice from the main installer — no separate command needed. Maker was previously called "Lite" and Developer was previously called "Standard"; the old names still work for pinned scripts and previously-installed users. -For scripts and docs that need to pin the choice up front (bypassing the in-VS-Code prompt), mode-specific shortcuts are available: +For scripts and docs that need to pin the choice up front (bypassing the terminal prompt), mode-specific shortcuts are available: **Windows** (PowerShell): @@ -132,11 +132,11 @@ cd ~/source/Employee-Self-Service-Agent-Developer-Kit/solutions/ess-maker-skills |---|---| | `Install-EssAdk.ps1` | Windows orchestrator. Installs toolchain via winget, pip dependencies, clones repo, installs extensions, launches VS Code. With `-FlightCheckOnly`, installs minimal toolchain and runs FlightCheck. | | `install-ess-adk.sh` | macOS orchestrator. Same as above but uses Homebrew. Set `FLIGHTCHECK_ONLY=true` for FlightCheck-only mode. | -| `bootstrap.ps1` | Windows one-liner entry point (prompts for Maker or Developer on first VS Code launch). | +| `bootstrap.ps1` | Windows one-liner entry point (asks Maker vs Developer in the terminal before VS Code launches). | | `bootstrap-lite.ps1` | Windows one-liner entry point (Maker mode - chat-first layout; previously named "Lite"). | | `bootstrap-dev.ps1` | Windows one-liner entry point (Developer mode - default VS Code layout; previously named "Standard"). | | `bootstrap-flightcheck.ps1` | Windows one-liner entry point (FlightCheck only). | -| `bootstrap-mac.sh` | macOS one-liner entry point (prompts for Maker or Developer on first VS Code launch). | +| `bootstrap-mac.sh` | macOS one-liner entry point (asks Maker vs Developer in the terminal before VS Code launches). | | `bootstrap-lite-mac.sh` | macOS one-liner entry point (Maker mode - chat-first layout; previously named "Lite"). | | `bootstrap-dev-mac.sh` | macOS one-liner entry point (Developer mode - default VS Code layout; previously named "Standard"). | | `bootstrap-flightcheck-mac.sh` | macOS one-liner entry point (FlightCheck only). | @@ -245,7 +245,7 @@ For air-gapped / locked-down environments, IT can mirror the files internally an From this folder: ```bash -# Full installer (prompts for Maker or Developer on first VS Code launch): +# Full installer (asks Maker or Developer in the terminal before VS Code launches): bash install-ess-adk.sh # Pin Maker mode (chat-first layout): diff --git a/setup/bootstrap-dev.ps1 b/setup/bootstrap-dev.ps1 index f884bab7c..2167d5be1 100644 --- a/setup/bootstrap-dev.ps1 +++ b/setup/bootstrap-dev.ps1 @@ -7,11 +7,11 @@ the maker the default VS Code layout (activity bar, file explorer, status bar visible) plus automatic /setup injection into the Copilot Chat side panel. This is the shortcut for makers who already know they - want the developer experience and want to skip the in-VS-Code mode - prompt on first launch. + want the developer experience and want to skip the Maker/Developer + terminal prompt. - New customers should use bootstrap.ps1, which prompts inside VS Code - on first launch and defaults to Maker (chat-first) mode. + New customers should use bootstrap.ps1, which asks in the terminal + which experience to install and defaults to Maker (chat-first) mode. iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-dev.ps1) diff --git a/setup/bootstrap-lite.ps1 b/setup/bootstrap-lite.ps1 index ef3cc23d9..a58290758 100644 --- a/setup/bootstrap-lite.ps1 +++ b/setup/bootstrap-lite.ps1 @@ -10,9 +10,9 @@ were merged into a single bootstrap.ps1 and the modes were renamed from lite/standard to maker/developer. - New customers should use bootstrap.ps1, which prompts inside VS Code - on first launch to pick the experience. This shim is documented as - a redirect only. + New customers should use bootstrap.ps1, which asks in the terminal + which experience to install. This shim is documented as a redirect + only. iex (irm https://raw.githubusercontent.com/microsoft/Employee-Self-Service-Agent-Developer-Kit/main/setup/bootstrap-lite.ps1) diff --git a/setup/bootstrap.ps1 b/setup/bootstrap.ps1 index 1a4190af8..430d37688 100644 --- a/setup/bootstrap.ps1 +++ b/setup/bootstrap.ps1 @@ -85,8 +85,8 @@ $scriptBlock = [ScriptBlock]::Create($scriptContent) $installerArgs = @{ Branch = $Branch } # Forward -InstallMode when the caller pinned one; otherwise leave the -# installer to fall back to its own default (prompt), which fires the -# in-VS-Code Maker/Developer QuickPick on first launch. +# installer to fall back to its own default (prompt), which asks the +# maker to pick Maker or Developer in the terminal before VS Code launches. if ($InstallMode) { $installerArgs.InstallMode = $InstallMode } if ($InstallRoot) { $installerArgs.InstallRoot = $InstallRoot } diff --git a/setup/install-ess-adk.sh b/setup/install-ess-adk.sh index a99e4d9d0..6eb5ac81c 100644 --- a/setup/install-ess-adk.sh +++ b/setup/install-ess-adk.sh @@ -25,7 +25,8 @@ FLIGHTCHECK_ONLY="${FLIGHTCHECK_ONLY:-false}" # INSTALL_MODE: maker | developer | prompt (or legacy lite | standard). # 'maker' (was 'lite') - chat-first layout, /setup after welcome wizard # 'developer' (was 'standard') - default VS Code layout, /setup via `code chat` -# 'prompt' - let the ESS Maker Profile ask on first launch +# 'prompt' - ask the maker in the terminal (defaults to +# maker under a non-interactive shell) # Legacy env var SKIP_MAKER_PROFILE=true is still accepted and mapped to # INSTALL_MODE=developer for back-compat with pinned CI scripts and old docs. INSTALL_MODE="${INSTALL_MODE:-}" @@ -69,9 +70,9 @@ if [[ "$INSTALL_MODE" == "prompt" ]]; then echo " chat and want a focused HR/IT admin surface." echo "" echo " [2] Developer" - echo " Default VS Code layout with GitHub Copilot Chat in the side" - echo " panel and /setup injected automatically. Best if you plan" - echo " to inspect or edit files directly." + echo " Default VS Code layout with GitHub Copilot Chat in the" + echo " side panel. Best if you plan to inspect or edit files" + echo " directly." echo "" while true; do printf "Enter 1 for Maker, 2 for Developer (default: 1): " @@ -474,10 +475,9 @@ if [[ "$FLIGHTCHECK_ONLY" != "true" ]]; then # ESS Maker Profile - installs in every mode. In maker mode it # applies the chat-first layout; in developer mode it only handles - # /setup injection (no visual changes); in prompt mode it asks the - # maker inside VS Code on first launch. The mode is communicated - # via essMaker.mode in VS Code's user settings.json (prompt maps - # to empty string, matching the Windows installer contract). + # /setup injection (no visual changes). By this point MODE_LABEL + # is always 'maker' or 'developer' - the CLI prompt at the top of + # the script resolves 'prompt' before we reach any install step. MODE_LABEL="$INSTALL_MODE" step "Installing ESS Maker Profile ($MODE_LABEL mode)" @@ -501,21 +501,14 @@ if [[ "$FLIGHTCHECK_ONLY" != "true" ]]; then fi # Write the mode setting so the extension knows whether to apply - # the lite layout or inject /setup (standard mode). + # the maker (chat-first) layout or inject /setup (developer mode). SETTINGS_DIR="$HOME/Library/Application Support/Code/User" if [[ "$(uname)" != "Darwin" ]]; then SETTINGS_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/Code/User" fi mkdir -p "$SETTINGS_DIR" SETTINGS_FILE="$SETTINGS_DIR/settings.json" - # In prompt mode, write an empty string so the ESS Maker Profile - # extension knows to show the QuickPick on first VS Code launch - # (matches the Windows installer contract). - if [[ "$MODE_LABEL" == "prompt" ]]; then - SETTINGS_MODE_VALUE="" - else - SETTINGS_MODE_VALUE="$MODE_LABEL" - fi + SETTINGS_MODE_VALUE="$MODE_LABEL" if [[ -f "$SETTINGS_FILE" ]]; then # Merge into existing settings using Python (available from step 2) python3 -c " @@ -779,8 +772,9 @@ if [[ -n "$CODE_CMD" ]]; then # (the default Copilot Chat experience). # - Maker mode: just open the workspace. The ESS Maker Profile extension # handles layout + /setup injection after the welcome wizard closes. - # - Prompt mode: open the workspace. The extension asks which experience - # the maker wants on first launch, then applies the chosen mode. + # By this point INSTALL_MODE is always 'maker' or 'developer' - the CLI + # prompt at the top of the script resolves 'prompt' before we reach any + # launch code. if [[ "$INSTALL_MODE" == "developer" ]]; then step "Opening workspace in VS Code and requesting /setup in Copilot Chat" if (cd "$WORKSPACE_PATH" && "$CODE_CMD" chat "/setup"); then @@ -793,23 +787,11 @@ if [[ -n "$CODE_CMD" ]]; then "$CODE_CMD" "$WORKSPACE_PATH" || warn "Could not launch VS Code. Open manually: $WORKSPACE_PATH" echo "Next: in VS Code, open Copilot Chat and run /setup to connect an editable DA Dev agent." fi - elif [[ "$INSTALL_MODE" == "maker" ]]; then - step "Opening workspace in VS Code" - if (cd "$WORKSPACE_PATH" && "$CODE_CMD" .); then - ok "Launched VS Code at $WORKSPACE_PATH" - echo -e " ${YELLOW}The ESS Maker Profile will run /setup in Copilot Chat after the welcome screen closes.${NC}" - echo -e " ${YELLOW}If VS Code prompts you to trust the workspace, accept the prompt.${NC}" - else - warn "Could not launch VS Code. Open manually: $WORKSPACE_PATH" - echo "Next: in VS Code, open Copilot Chat and run /setup to connect an editable DA Dev agent." - fi else - # Prompt mode - extension will ask which experience to apply - # on first launch, then run the appropriate flow. step "Opening workspace in VS Code" if (cd "$WORKSPACE_PATH" && "$CODE_CMD" .); then ok "Launched VS Code at $WORKSPACE_PATH" - echo -e " ${YELLOW}VS Code will ask you to pick a chat-first (Maker) or default developer experience on first launch.${NC}" + echo -e " ${YELLOW}The ESS Maker Profile will run /setup in Copilot Chat after the welcome screen closes.${NC}" echo -e " ${YELLOW}If VS Code prompts you to trust the workspace, accept the prompt.${NC}" else warn "Could not launch VS Code. Open manually: $WORKSPACE_PATH" diff --git a/setup/telemetry/install-telemetry.ps1 b/setup/telemetry/install-telemetry.ps1 index 658243a56..047443bfd 100644 --- a/setup/telemetry/install-telemetry.ps1 +++ b/setup/telemetry/install-telemetry.ps1 @@ -227,8 +227,8 @@ function Initialize-EssInstallTelemetry { 'adk' with -InstallMode maker instead. .PARAMETER InstallMode The VS Code experience the maker will land in after the installer completes: maker | developer | prompt (or the - legacy aliases 'lite' | 'standard'). 'prompt' means the ESS Maker - Profile extension will ask on first launch. + legacy aliases 'lite' | 'standard'). 'prompt' means the installer + will ask in the terminal before VS Code launches. #> param( [ValidateSet('adk', 'lite', 'flightcheck')] diff --git a/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix b/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix index 6932f095bf3e77afac51154dd4fd2f39b90171b6..1825456bc1b216f032eb751e59d5e0d8e60bef29 100644 GIT binary patch delta 2222 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 delta 2188 zcmYjSX*d)L7oKM9W+EoWPT?X;M#f%J*~TDy_NL|{O9~*Mx$i)h92;b{KAUN* zK2_#dRYCdY)s#N5QcZLvdAf8WQ`W@N?zM8xX3h$We1ty)@2Zx^?ttIbUn-Vw4#!{v z6`m|TnFQA>&<>L2A-Mhc{OEc7eTIuhRHXIhne|Uy!bh?$Xb3%aw3mpp{T$c6Gx?+C z2@~y|t=30*Ujxivv`~EhFjQe)jBM;GQc17%s1}Pt7S=NVX&z)LJTdHn@^+rkl6*&x--44;3xey5r|yB3 zSN@H0jc-4UQ(L(uTe~%G*S+K>PH#wHcMk@5iW*V->$7$i5fDj)jS+IWRIfX%%gTkA zIX9KGe%)jSxB3V15v)hf_EHH$CfjZ#k-fO~bIgF0O0F-7E+Lq zXPRr4n!<9AiUD)j<4QBz=N$*~xdkueha&Oyw}BX-vc)N784n(YH35@>HYDuq;zvtw9F$njL3pD1I9O#BtcUF{BG`6NV7b&B8#DjzYQkT z@JCYD=J}IL`1l<+xxda}OH{(GVlJE5ebZ}HHkE?yoty90NH<>-(w}&BbDn zT!zF-;(ZaP&?&sL+3TW-Ie7D*O5wzU-e=btue7IrZqAQPe(xJ|e!A<5jl5^a8RI)P z-;lBLhpCS@Bm~M?G?Devcv*>fnArQ38BQtK({LNgY)qCk&_Okcb~HO2xX*-%`^x@Q zu|%?SRIbuaR#^f7Ovw{~fBet!%m2d2Urv|D=FHrkMDPyGaBpKZIY2(3mQhHbh%7NZ!RWmBrjnVxfh1PciBie4_FTs|D3!D{%ZYhp%y&s-LK|>(nyrCC1Gx3 zLj?YMcFn?5-C#v`8Aegx)|lYZ7S!wXah%t_>QF52!M*K0%28wRPOGykcc~BUy?xVR z*@WrK92%RA5Xr$Aq`Ddkk504NZ-K8+ghL(AWdkTiYp+V`$Y^p=4|`b7N_@yQ{MK;8 z_sSW%1zf0YeW)2-TTr>MIY0~OX{y3@I`7)0mL}vporRR0{(5+nZQvyU4&0#&D{YuB zti@$5QM%8Z%Yb$e?s!a$!MDWgLQrKsL^W=ofslcuE>uMJfu|suxE{-PMHX=oaR6TY z;P?r=23XJQBtaaf?}_k@uG{y2p@rDqHxiy^Fy`W6-Gd=Z@OcM0IL_0ulEK z*3Z1O=JwNHkJ=*Ni^|n`hgaM$%x?5meM7bSo7&wd$ggi0aYBr|(c>TvOJ_K3+TYsP z<_gQC^33>2G#n&N>h0+*Btf6{EY_p0iZ~DI@`Q;`fsP{WB}DvWhOsIc+gzV|mMfO# zAVwJFOCS0K-&7YWRpVVDgUc7Ehw19xT?GNMQ}0I1v$vk*$R+XfQwVk932_KS=I7cK zLH-ehv+Ney*s)=6N06ZFF@9hTb*S-l$R>+FT3dNFj%b9lCGY{^iaIpm%jk+|aMa1N z1bE!0AuEit#}gQc4dBP!ZlJe4UI4|LEYY-N8*BY~2$*76@x3#aWmz0qN}~fhJ?tWIHXDan zQe&!=qsx;2jkB=Y#|&K6Fe5&u8GZ^Rm&U7!Dp7BVOV7p{&s73AJBOzy{0!qjoh7gN zjKRyGT3)9UXJ656Wy&mS#p&RMjznEO8y|(HgSsqTX}#}nUpi@f_`7AU*$x%1GHs~N zlnycOF5V)8O!vm>>sLd15$5LzcUBJj(~46`98mWZ^$B1>C4TLRvHx2Nv?_u2j5f%p zC+9EV@fAzXNRuxY708A9Rh#LP7g8= zgEd^-o{#IVG3iP5xY^l|C6CgyDElEbTu^6iIgL4U`QprxKKX|h#|ND;q27+Qr2hW= zPTPw#%Dh{K?js`%3>BDB(Lkn^lEQWGWVu#zUHHD+GcghbZI9H>Gn>X7-Tg3c)S4!7 z1Ue3`0HhadItu^*Jqh?;g{Qo8az`s=H zpBM$;n~>kKF-8+85cZ3nB|u^ofbfXl)9bOSz~RW>R3(aCz!J&Ic6<%|tM7mS07&$) F`Y*cF{`vp_ diff --git a/tools/ess-maker-profile/extension/extension.js b/tools/ess-maker-profile/extension/extension.js index 1b0716c83..0428eb824 100644 --- a/tools/ess-maker-profile/extension/extension.js +++ b/tools/ess-maker-profile/extension/extension.js @@ -1580,9 +1580,12 @@ function activate(context) { // normalized to 'maker'/'developer' in firstInstallDispatch. // Maker mode: applies chat-only layout; user clicks Setup to run /setup. // Developer mode: injects /setup into Copilot Chat automatically. - // Empty ("") / "prompt": consolidated installer (ADO #7895603) left - // the choice to us - show a QuickPick, default to maker, then - // route into the chosen branch. + // Empty ("") / "prompt": installer left the choice unresolved (e.g. a + // maker double-clicked the extension into a stray VS Code window + // without running the consolidated installer). firstInstallDispatch + // silently defaults to maker; we never show a first-launch modal + // because that surface reliably loses the race against the theme + // picker and Copilot sign-in. // - Subsequent maker-mode activations: silently re-apply layout. const alreadyApplied = context.globalState.get(APPLIED_KEY, false); const userWantsMakerLayout = context.globalState.get(LITE_MODE_KEY, true); // default to maker layout @@ -1592,8 +1595,8 @@ function activate(context) { if (vscode.workspace.workspaceFolders?.length) { if (!alreadyApplied) { - // First install. Resolve mode (prompting the maker if the - // consolidated installer didn't pin it), then dispatch. + // First install. Silently resolve mode (defaulting to maker if + // the installer left essMaker.mode blank/"prompt"), then dispatch. firstInstallDispatch(context, installerMode) .catch(err => _log(`activate: firstInstallDispatch error: ${err && err.message}`)); } else if (userWantsMakerLayout) { diff --git a/tools/ess-maker-profile/extension/package.json b/tools/ess-maker-profile/extension/package.json index 9f9f6b745..399991ad3 100644 --- a/tools/ess-maker-profile/extension/package.json +++ b/tools/ess-maker-profile/extension/package.json @@ -94,7 +94,7 @@ "type": "string", "enum": ["maker", "developer", "lite", "standard", ""], "default": "", - "description": "Install mode set by the ESS installer. 'maker' (was 'lite') applies the chat-first layout; 'developer' (was 'standard') uses the default VS Code layout with /setup injection; empty string means the consolidated installer left the choice unset and the extension will ask on first launch. The legacy 'lite'/'standard' values are still accepted and treated as 'maker'/'developer' so existing users keep their choice after the rename." + "description": "Install mode set by the ESS installer. 'maker' (was 'lite') applies the chat-first layout; 'developer' (was 'standard') uses the default VS Code layout with /setup injection; empty string means the installer left the choice unresolved and the extension silently defaults to maker on first activation. The legacy 'lite'/'standard' values are still accepted and treated as 'maker'/'developer' so existing users keep their choice after the rename." }, "essMaker.autoUpdateCheck": { "type": "boolean", From 7871fcc8dec77d0610dff744308c6c8dd7b2a294 Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Thu, 24 Sep 2026 11:54:17 -0700 Subject: [PATCH 07/11] installer: drop 'QuickPick' from code/test/CHANGELOG copy Rewords the code comments in Install-EssAdk.ps1 and install-ess-adk.sh, the test name/comment in Install-EssAdk.Tests.ps1 + extension.test.js, and CHANGELOG entries 0.4.26-0.4.28 so the shipped copy just describes how mode selection works today (installer terminal prompt -> writes essMaker.mode, extension trusts it) without referencing a QuickPick. Behavior unchanged; tests still 62/62 (PS) + 46/46 (extension). The extension.test.js guardrail regex still blocks 'showQuickPick(...Maker' from regressing into extension.js. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- setup/Install-EssAdk.Tests.ps1 | 11 ++-- setup/Install-EssAdk.ps1 | 10 ++- setup/install-ess-adk.sh | 9 ++- .../ess-maker-profile/extension/CHANGELOG.md | 65 ++++++++----------- .../extension/extension.test.js | 17 +++-- 5 files changed, 50 insertions(+), 62 deletions(-) diff --git a/setup/Install-EssAdk.Tests.ps1 b/setup/Install-EssAdk.Tests.ps1 index 742ddae0e..a14e82625 100644 --- a/setup/Install-EssAdk.Tests.ps1 +++ b/setup/Install-EssAdk.Tests.ps1 @@ -70,12 +70,11 @@ Test 'legacy InstallMode values (lite/standard) are coerced to maker/developer' } } -Test 'InstallMode=prompt fires a terminal Maker/Developer prompt (not a VS Code QuickPick)' { - # Historically we tried to ask the mode question from inside VS Code - # via a QuickPick, but that surface competes with the theme picker and - # Copilot sign-in on first launch and reliably loses the race. The - # installer now asks the question in the terminal before handing off - # to VS Code so the answer is deterministic. +Test 'InstallMode=prompt fires a Maker/Developer prompt in the installer terminal' { + # The installer resolves the mode in the terminal before handing off + # to VS Code so essMaker.mode is written to settings.json before any + # editor UI appears - the answer never races with the theme picker + # or GitHub Copilot sign-in that VS Code renders on first launch. if ($src -notmatch "if\s*\(\`$InstallMode\s+-eq\s+'prompt'\)") { throw 'no prompt branch guarding InstallMode=prompt' } diff --git a/setup/Install-EssAdk.ps1 b/setup/Install-EssAdk.ps1 index bcef30198..a04c937bc 100644 --- a/setup/Install-EssAdk.ps1 +++ b/setup/Install-EssAdk.ps1 @@ -112,12 +112,10 @@ if ($InstallMode -eq 'standard') { $InstallMode = 'developer' } # When the caller didn't pin a mode (the default one-liner path via # bootstrap.ps1), prompt the maker in the terminal for their preference. -# Historically we tried to prompt from inside VS Code via a QuickPick, -# but that surface competes with the theme picker + GitHub Copilot -# sign-in on first launch and reliably loses the race - the maker never -# sees it. Prompting here in the CLI, before we hand off to VS Code, -# means the choice is applied deterministically before any editor UI -# appears. +# Doing it here in the CLI, before we hand off to VS Code, makes the +# choice deterministic: the answer is applied to essMaker.mode before +# any editor UI appears, so there's no race with the theme picker or +# GitHub Copilot sign-in that VS Code renders on first launch. if ($InstallMode -eq 'prompt') { $nonInteractive = $env:CI -or $env:TF_BUILD -or $env:GITHUB_ACTIONS -or [Console]::IsInputRedirected if ($nonInteractive) { diff --git a/setup/install-ess-adk.sh b/setup/install-ess-adk.sh index 6eb5ac81c..a29a8ee7a 100644 --- a/setup/install-ess-adk.sh +++ b/setup/install-ess-adk.sh @@ -51,11 +51,10 @@ esac # When the caller didn't pin a mode (the default one-liner path via # bootstrap-mac.sh), prompt the maker in the terminal for their preference. -# Historically we tried to prompt from inside VS Code via a QuickPick, but -# that surface competes with the theme picker + GitHub Copilot sign-in on -# first launch and reliably loses the race - the maker never sees it. -# Prompting here in the CLI, before we hand off to VS Code, means the -# choice is applied deterministically before any editor UI appears. +# Doing it here in the CLI, before we hand off to VS Code, makes the +# choice deterministic: the answer is applied to essMaker.mode before +# any editor UI appears, so there's no race with the theme picker or +# GitHub Copilot sign-in that VS Code renders on first launch. if [[ "$INSTALL_MODE" == "prompt" ]]; then if [[ -n "${CI:-}" || -n "${TF_BUILD:-}" || -n "${GITHUB_ACTIONS:-}" ]] || [[ ! -t 0 ]]; then echo "" diff --git a/tools/ess-maker-profile/extension/CHANGELOG.md b/tools/ess-maker-profile/extension/CHANGELOG.md index 46a30c7d7..479a97e41 100644 --- a/tools/ess-maker-profile/extension/CHANGELOG.md +++ b/tools/ess-maker-profile/extension/CHANGELOG.md @@ -2,19 +2,14 @@ ## 0.4.28 (POC) -- **Mode prompt moved from VS Code to the installer CLI.** The 0.4.27 - first-launch Maker/Developer QuickPick reliably lost the race against - the theme picker and Copilot sign-in on first VS Code launch, so - makers never actually saw it and were silently landed in Maker mode - regardless of intent. The consolidated installer +- **Mode prompt lives in the installer CLI.** The consolidated installer (`setup/Install-EssAdk.ps1` on Windows, `setup/install-ess-adk.sh` on - macOS) now asks the question in the terminal before it launches VS - Code, and writes the resolved mode to `essMaker.mode` in - `settings.json`. The extension trusts that value; the in-VS-Code - QuickPick and `promptForInstallMode()` helper are removed. Blank / - `prompt` values still default silently to Maker (no first-launch - modal), preserving deterministic behavior for stray VS Code windows - that never went through the installer. + macOS) asks the Maker/Developer question in the terminal before it + launches VS Code, and writes the resolved mode to `essMaker.mode` in + `settings.json`. The extension trusts that value: blank / `prompt` + values silently default to Maker on first activation, so a stray VS + Code window that never went through the installer still lands + deterministically without any first-launch modal. ## 0.4.27 (POC) @@ -22,35 +17,29 @@ User research showed "Lite" undersold the mode (it is a full, streamlined chat-first UX, not a cut-down one) and "Standard" was not really standard (it still opens Copilot Chat and runs `/setup` on - first launch). The first-launch QuickPick now offers **Maker - (recommended)** and **Developer**, dismissing defaults to Maker, - and a new `setup/bootstrap-dev.ps1` shortcut mirrors the existing - `bootstrap-lite.ps1`. The `essMaker.mode` setting accepts the new - values (`maker`, `developer`) and still accepts the legacy values - (`lite`, `standard`) so pinned CI, docs, and previously-installed - users are not disrupted; legacy values are normalized on read at the - top of `firstInstallDispatch`. The `essMaker.restoreStandardLayout` - command ID is preserved (its user-facing title is now "Restore - Developer Layout"). The persisted on-disk key `essMaker.liteMode.v1` - is likewise preserved so existing users do not lose their layout - preference. macOS gets a matching `bootstrap-dev-mac.sh` shortcut; - `bootstrap-lite-mac.sh` continues to work and pins Maker mode. The - installer emits an additional `installMode` telemetry dimension - (maker | developer | prompt, or legacy lite | standard). + first launch). A new `setup/bootstrap-dev.ps1` shortcut mirrors the + existing `bootstrap-lite.ps1`. The `essMaker.mode` setting accepts + the new values (`maker`, `developer`) and still accepts the legacy + values (`lite`, `standard`) so pinned CI, docs, and previously- + installed users are not disrupted; legacy values are normalized on + read at the top of `firstInstallDispatch`. The + `essMaker.restoreStandardLayout` command ID is preserved (its + user-facing title is now "Restore Developer Layout"). The persisted + on-disk key `essMaker.liteMode.v1` is likewise preserved so existing + users do not lose their layout preference. macOS gets a matching + `bootstrap-dev-mac.sh` shortcut; `bootstrap-lite-mac.sh` continues + to work and pins Maker mode. The installer emits an additional + `installMode` telemetry dimension (maker | developer | prompt, or + legacy lite | standard). ## 0.4.26 (POC) -- **First-launch mode prompt.** After the consolidated Windows installer - (see ADO #7895603 - one `bootstrap.ps1` for both experiences), the - extension asks the maker on first VS Code launch which experience they - want: **Standard (recommended)** - default developer view with GitHub - Copilot Chat in the side panel and automatic `/setup` - or **Lite - (chat-first)** - hidden developer chrome and the Quick Actions rail. - The choice is persisted to the `essMaker.mode` global setting so - subsequent launches skip the prompt; dismissing the QuickPick defaults - to Standard. Legacy invocations that pin `essMaker.mode` to `lite` or - `standard` (including the back-compat `bootstrap-lite.ps1` shim) - bypass the prompt. +- **Windows installer consolidation.** One `bootstrap.ps1` drives both + experiences (see ADO #7895603) and forwards the chosen mode into the + extension via the `essMaker.mode` global setting so subsequent + launches skip any prompt. Legacy invocations that pin + `essMaker.mode` to `lite` or `standard` (including the back-compat + `bootstrap-lite.ps1` shim) bypass the terminal prompt. ## 0.4.25 (POC) diff --git a/tools/ess-maker-profile/extension/extension.test.js b/tools/ess-maker-profile/extension/extension.test.js index 3ebdb800f..c144a1351 100644 --- a/tools/ess-maker-profile/extension/extension.test.js +++ b/tools/ess-maker-profile/extension/extension.test.js @@ -202,13 +202,16 @@ test('exposes the essMaker.autoUpdateCheck opt-out setting', () => { console.log('\nfirst-install mode dispatch (ADO #7895603):'); -test('extension source does NOT declare an in-VS-Code mode QuickPick', () => { - // The mode is now resolved in the installer CLI before VS Code launches - // (setup/Install-EssAdk.ps1 + install-ess-adk.sh prompt there). A VS Code - // QuickPick on first launch reliably loses the race against the theme - // picker + Copilot sign-in, so it was removed. - assert.ok(!/async function promptForInstallMode/.test(src), 'promptForInstallMode should be gone; the installer prompts in the CLI'); - assert.ok(!/showQuickPick\([\s\S]{0,200}?Maker \(recommended\)/.test(src), 'in-VS-Code Maker/Developer QuickPick should be removed'); +test('extension does not prompt for mode inside VS Code', () => { + // The mode is resolved in the installer CLI before VS Code launches + // (setup/Install-EssAdk.ps1 + install-ess-adk.sh prompt there), so + // essMaker.mode is already written to settings.json by the time the + // extension activates. If the extension prompted here on first + // launch, its picker would race the theme picker + Copilot sign-in + // that VS Code renders on first launch, so we guard against a mode + // picker regressing into the extension. + assert.ok(!/async function promptForInstallMode/.test(src), 'promptForInstallMode should not exist; the installer prompts in the CLI'); + assert.ok(!/showQuickPick\([\s\S]{0,200}?Maker \(recommended\)/.test(src), 'in-VS-Code Maker/Developer picker should not exist'); }); test('firstInstallDispatch defaults blank / "prompt" installer values to maker', () => { From 862e9ceadf188ffab820e8d8cbfb8f54db6e410e Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Thu, 24 Sep 2026 13:25:32 -0700 Subject: [PATCH 08/11] installer: address PR 296 review comments (F-1 through F-6) F-1 (critical): replace PS7-only `\ ?? ''` with an explicit `if (\ -eq \)` null check so Windows PowerShell 5.1's parser doesn't reject Install-EssAdk.ps1 with 'Unexpected token'.' Adds two tests: one invokes powershell.exe 5.1 explicitly and runs [Parser]::ParseFile against Install-EssAdk.ps1; the other regexes `??` out of the source (comment-stripped) as a direct guard rail. F-2 (major): remove `export SKIP_MAKER_PROFILE=true` from bootstrap-mac.sh so the primary macOS one-liner actually reaches the new terminal Maker/Developer prompt instead of forcing developer. F-3 (major): stop the extension's developer-mode branch from calling injectSetup/waitForWelcomeWizard - the installer already runs `code chat '/setup'` before launching VS Code, so the extension was opening a second /setup chat on the fresh-install path. Adds a regression-guard test that asserts the isDeveloperMode branch of firstInstallDispatch calls neither injectSetup nor waitForWelcomeWizard. F-5 (minor): unify legacy-option precedence across platforms. SKIP_MAKER_PROFILE=true on macOS now always coerces to INSTALL_MODE=developer even when INSTALL_MODE is also set, matching the Windows -SkipMakerProfile switch's precedence, so pinned CI on either platform gets the same answer. F-6 (nit): bootstrap-dev.ps1 + bootstrap-dev-mac.sh comments now say 'skip the terminal mode prompt', not 'skip the in-VS-Code mode prompt'. F-4 will be addressed by rebasing on origin/main in a follow-up push. Rebuilt ess-maker-profile-0.4.28.vsix with the extension.js change. Tests: 64/64 (PS installer), 47/47 (extension). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- setup/Install-EssAdk.Tests.ps1 | 47 ++++++++++++++++++ setup/Install-EssAdk.ps1 | 6 ++- setup/bootstrap-dev-mac.sh | 6 +-- setup/bootstrap-dev.ps1 | 8 +-- setup/bootstrap-mac.sh | 2 - setup/install-ess-adk.sh | 8 +-- .../extension/ess-maker-profile-0.4.28.vsix | Bin 33209 -> 32896 bytes .../ess-maker-profile/extension/extension.js | 20 +++----- .../extension/extension.test.js | 12 +++++ 9 files changed, 82 insertions(+), 27 deletions(-) diff --git a/setup/Install-EssAdk.Tests.ps1 b/setup/Install-EssAdk.Tests.ps1 index a14e82625..99cdfa329 100644 --- a/setup/Install-EssAdk.Tests.ps1 +++ b/setup/Install-EssAdk.Tests.ps1 @@ -93,6 +93,53 @@ Test 'InstallMode=prompt defaults to maker under non-interactive stdin / CI' { } } +Test 'Install-EssAdk.ps1 parses under Windows PowerShell 5.1' { + # F-1: the supported Windows path invokes Windows PowerShell 5.1 - if + # anything in the script uses a PS7-only operator (``??``, ``?.``, + # pipeline chain ``||`` / ``&&``, ternary) the 5.1 parser rejects the + # whole file before running a line. Testing under $PSVersionTable + # (which is PS7 in this suite) catches nothing; we have to invoke + # ``powershell.exe`` (5.1) explicitly with -NoProfile -Command and let + # its Language.Parser::ParseFile look at the file. + $pwsh5 = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe' + if (-not (Test-Path $pwsh5)) { + # Non-Windows test host: skip (macOS / Linux CI). Windows CI hits this. + return + } + $scriptPath = Join-Path $PSScriptRoot 'Install-EssAdk.ps1' + $probe = @' +$errors = $null +$tokens = $null +[System.Management.Automation.Language.Parser]::ParseFile($args[0], [ref]$tokens, [ref]$errors) | Out-Null +if ($errors.Count -gt 0) { + $errors | ForEach-Object { Write-Output $_.Message } + exit 1 +} +exit 0 +'@ + $probeFile = Join-Path $env:TEMP 'ess-adk-ps51-parse-probe.ps1' + Set-Content -LiteralPath $probeFile -Value $probe -Encoding ASCII + try { + $result = & $pwsh5 -NoProfile -ExecutionPolicy Bypass -File $probeFile $scriptPath 2>&1 + if ($LASTEXITCODE -ne 0) { + throw "PS 5.1 parser rejected Install-EssAdk.ps1: $($result -join '; ')" + } + } finally { + Remove-Item -LiteralPath $probeFile -ErrorAction SilentlyContinue + } +} + +Test 'installer answer-normalization is PS 5.1 compatible (no ??)' { + # Direct guard rail for F-1: fail loudly if a future edit reintroduces + # ``??`` in executable code. Strip PowerShell single-line comments + # first so the guard-rail commentary in the script itself does not + # trip this check. + $stripped = ($src -split "`n" | ForEach-Object { ($_ -replace '#.*$', '') }) -join "`n" + if ($stripped -match '\?\?') { + throw 'Install-EssAdk.ps1 must not use PS7-only ``??`` (breaks Windows PowerShell 5.1). Use ``if ($null -eq ...) { ... }`` instead.' + } +} + Test 'script declares SkipLaunch parameter' { if ($src -notmatch '\[switch\]\s*\$SkipLaunch') { throw 'SkipLaunch switch parameter not found' diff --git a/setup/Install-EssAdk.ps1 b/setup/Install-EssAdk.ps1 index a04c937bc..3f5e7285a 100644 --- a/setup/Install-EssAdk.ps1 +++ b/setup/Install-EssAdk.ps1 @@ -138,7 +138,11 @@ if ($InstallMode -eq 'prompt') { $choice = $null while ($null -eq $choice) { $answer = Read-Host "Enter 1 for Maker, 2 for Developer (default: 1)" - $answer = ($answer ?? '').Trim() + # ``$answer ?? ''`` would need PS7's null-coalescing operator, but + # the supported Windows path invokes Windows PowerShell 5.1 - the + # 5.1 parser rejects ``??`` before running a line of the installer. + if ($null -eq $answer) { $answer = '' } + $answer = $answer.Trim() switch -Regex ($answer) { '^(1|maker|m|)$' { $choice = 'maker' } '^(2|developer|dev|d)$' { $choice = 'developer' } diff --git a/setup/bootstrap-dev-mac.sh b/setup/bootstrap-dev-mac.sh index 0843d9d70..d4843c979 100644 --- a/setup/bootstrap-dev-mac.sh +++ b/setup/bootstrap-dev-mac.sh @@ -8,13 +8,13 @@ # Installs the full maker kit and lands the maker in the default VS Code # layout (activity bar, file explorer, status bar visible) with /setup # injected into Copilot Chat. Shortcut for makers who already know they want -# the Developer experience and want to skip the in-VS-Code mode prompt. +# the Developer experience and want to skip the terminal mode prompt. # --------------------------------------------------------------------------- set -euo pipefail # Developer mode: pin INSTALL_MODE so install-ess-adk.sh uses `code chat` -# to open /setup in the sidebar panel and does not apply the chat-first -# layout. +# to open /setup in the sidebar panel, skips the terminal mode prompt, and +# does not apply the chat-first layout. export INSTALL_MODE="developer" # Parse optional --branch / --source-base-url arguments diff --git a/setup/bootstrap-dev.ps1 b/setup/bootstrap-dev.ps1 index 2167d5be1..635fbb285 100644 --- a/setup/bootstrap-dev.ps1 +++ b/setup/bootstrap-dev.ps1 @@ -88,10 +88,10 @@ $installer = Join-Path $tempDir 'Install-EssAdk.ps1' $scriptContent = [System.IO.File]::ReadAllText($installer, [System.Text.Encoding]::UTF8) $scriptBlock = [ScriptBlock]::Create($scriptContent) -# Developer mode: pass -InstallMode developer so the installer skips the -# in-VS-Code mode prompt and lands directly in the default VS Code layout -# with /setup requested via `code chat`. Same physical installer as the -# other bootstraps; just a different pinned mode. +# Developer mode: pass -InstallMode developer so the installer's terminal +# mode prompt is skipped and the maker lands directly in the default VS Code +# layout with /setup requested via `code chat`. Same physical installer as +# the other bootstraps; just a different pinned mode. $installerArgs = @{ Branch = $Branch; InstallMode = 'developer' } if ($InstallRoot) { $installerArgs.InstallRoot = $InstallRoot } diff --git a/setup/bootstrap-mac.sh b/setup/bootstrap-mac.sh index 08a0b7e39..bdae27bd9 100644 --- a/setup/bootstrap-mac.sh +++ b/setup/bootstrap-mac.sh @@ -7,8 +7,6 @@ # --------------------------------------------------------------------------- set -euo pipefail -export SKIP_MAKER_PROFILE="true" - # Parse optional --branch / --source-base-url arguments BRANCH="main" SOURCE_BASE_URL="" diff --git a/setup/install-ess-adk.sh b/setup/install-ess-adk.sh index a29a8ee7a..302d4c44c 100644 --- a/setup/install-ess-adk.sh +++ b/setup/install-ess-adk.sh @@ -27,11 +27,13 @@ FLIGHTCHECK_ONLY="${FLIGHTCHECK_ONLY:-false}" # 'developer' (was 'standard') - default VS Code layout, /setup via `code chat` # 'prompt' - ask the maker in the terminal (defaults to # maker under a non-interactive shell) -# Legacy env var SKIP_MAKER_PROFILE=true is still accepted and mapped to -# INSTALL_MODE=developer for back-compat with pinned CI scripts and old docs. +# Legacy env var SKIP_MAKER_PROFILE=true is still accepted and, matching the +# Windows -SkipMakerProfile switch's precedence, always coerces to +# INSTALL_MODE=developer even when INSTALL_MODE is also set - so pinned CI +# scripts on either platform get the same answer. INSTALL_MODE="${INSTALL_MODE:-}" SKIP_MAKER_PROFILE="${SKIP_MAKER_PROFILE:-false}" -if [[ -z "$INSTALL_MODE" && "$SKIP_MAKER_PROFILE" == "true" ]]; then +if [[ "$SKIP_MAKER_PROFILE" == "true" ]]; then INSTALL_MODE="developer" fi if [[ -z "$INSTALL_MODE" ]]; then diff --git a/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix b/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix index 1825456bc1b216f032eb751e59d5e0d8e60bef29..96f1dc29e47677be5b14b2dee1926672c1141e9b 100644 GIT binary patch delta 25356 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 LVgG^P|5*PEA*#f4 delta 25576 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 DCcX`D diff --git a/tools/ess-maker-profile/extension/extension.js b/tools/ess-maker-profile/extension/extension.js index 0428eb824..139b293a3 100644 --- a/tools/ess-maker-profile/extension/extension.js +++ b/tools/ess-maker-profile/extension/extension.js @@ -1492,21 +1492,13 @@ async function firstInstallDispatch(context, installerMode) { _log(`firstInstallDispatch: alreadyConfigured=${alreadyConfigured}`); if (isDeveloperMode) { - // Developer mode: no layout changes. In prompt mode the installer - // could not eagerly run `code chat '/setup'` (mode was unknown at - // launch time), so surface /setup here for the not-yet-configured - // path - otherwise a maker who picks Developer here would land in - // an empty chat with no cue what to do next. + // Developer mode: no layout changes. The installer already dispatched + // ``code chat "/setup"`` before launching VS Code (Install-EssAdk.ps1 + // and install-ess-adk.sh both do this for the developer branch), so + // the extension deliberately does NOT inject /setup again here - + // doing so would open two /setup chats on the fresh-install path. context.globalState.update(APPLIED_KEY, true); - if (!alreadyConfigured) { - _log('firstInstallDispatch: developer mode, running /setup via injectSetup'); - waitForWelcomeWizard() - .then(() => new Promise(r => setTimeout(r, 3000))) - .then(() => injectSetup()) - .catch((err) => _log(`firstInstallDispatch: developer injectSetup error: ${err && err.message}`)); - } else { - _log('firstInstallDispatch: developer mode, already configured - no action'); - } + _log(`firstInstallDispatch: developer mode, alreadyConfigured=${alreadyConfigured} - installer owns /setup dispatch, extension no-ops`); return; } diff --git a/tools/ess-maker-profile/extension/extension.test.js b/tools/ess-maker-profile/extension/extension.test.js index c144a1351..e82081823 100644 --- a/tools/ess-maker-profile/extension/extension.test.js +++ b/tools/ess-maker-profile/extension/extension.test.js @@ -223,6 +223,18 @@ test('firstInstallDispatch persists the resolved mode to global settings', () => assert.ok(/'essMaker\.mode',[\s\S]*?ConfigurationTarget\.Global/.test(src), 'resolved mode should be persisted with ConfigurationTarget.Global'); }); +test('developer mode does NOT inject /setup from the extension (installer owns dispatch)', () => { + // F-3 regression guard: the installer already runs ``code chat "/setup"`` + // for developer mode before launching VS Code, so injecting again in + // the extension opens two /setup chats on the fresh-install path. + // The isDeveloperMode branch of firstInstallDispatch must therefore + // not call injectSetup / waitForWelcomeWizard. + const devBranch = src.match(/if\s*\(isDeveloperMode\)\s*\{([\s\S]*?)\n\s*return;\s*\n\s*\}/); + assert.ok(devBranch, 'isDeveloperMode branch not found in firstInstallDispatch'); + assert.ok(!/injectSetup\s*\(/.test(devBranch[1]), 'developer branch must not call injectSetup - installer owns /setup dispatch'); + assert.ok(!/waitForWelcomeWizard\s*\(/.test(devBranch[1]), 'developer branch must not wait for welcome wizard to inject /setup'); +}); + test('legacy essMaker.mode values are normalized (lite -> maker, standard -> developer)', () => { assert.ok(/function normalizeInstallerMode/.test(src), 'normalizeInstallerMode helper missing'); assert.ok(/if\s*\(mode === 'lite'\)\s*return 'maker'/.test(src), "'lite' should be normalized to 'maker'"); From 1f0248310f2f361236ad748676aca22427f75b0b Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Thu, 24 Sep 2026 14:02:04 -0700 Subject: [PATCH 09/11] installer: align extension comment with installer-owned /setup dispatch Address PR 296 nit from re-review: extension.js activate() comment still said 'Developer mode: injects /setup into Copilot Chat automatically', which contradicts the new responsibility boundary (F-3 fix). Reworded to say the installer dispatches /setup before VS Code launches and the extension only records the mode. Rebuilt ess-maker-profile-0.4.28.vsix with the updated comment. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- .../extension/ess-maker-profile-0.4.28.vsix | Bin 32896 -> 32934 bytes .../ess-maker-profile/extension/extension.js | 4 +++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix b/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix index 96f1dc29e47677be5b14b2dee1926672c1141e9b..efebdfcf06c8483fa9177758a54d94d192b61944 100644 GIT binary patch delta 1060 zcmV+<1l#+7fdZz10t`?~0|XQR00;;GJ8-cK$pQj9aFf>p?*cn;v&aJ-76LDDv*#Jk zO@HzgmI0#&Y3<)S|LlO;1sL{Ci}+H%L>MzMl2EL{`WisuuugfBz@6c^E)QLuD2NuY8(jM&~ZL4!RcaZqT^t z&REvwt&PjdnwFn}-CKLTAR&X(P@cQZ8GmRfM!Z0e8_#W6sOOARjovun`C`{V-W z7@n0Ul!6CnyRGG2Z96_iF=((NXrY>jaF5!emfD18Ab=E4&pP0Bjn-AXl*r!kBzLTRd4bUQ`&#@7dQRJ74O`;Zh!jv zrs7W8sk=A&O)A-{tR#n#*k=Dski3fNgmY~+ukE1|(S=cZ1dZx23SG^|8?IV*v6B1ThwV|aZ)5mnqVFpV zU-dKXUSiD^U6BClT@82=LRqN^bRlakQGihu$-*1Tq|HT8YzechF~eZ4+kcM%;YOjT zr1ZFk@#cyKRZCixqAMvvnE)X{giFcUgZtE^U!)?7WJo21KtPFQG8x1XNCQeJ42NZ1 zbF1SaM=!t_!atnxLcUNvMs^X1KENF}fd?k%#K-`N5EzEQQDfv86(;t^!vLKt4n~#& zZeem#PYSTxMb$zOtc!vaMt=lHN#7etXZ#MJ3n#?TsT49MTk93{bu_!m!OIRYL#ar- z0dZx$fB_?spdetaE?*>462jm?hJ&%N&D|A7i&uSmI(rO`IjCAn%(+>MUM#1u`IY;x4)=fjc=Y!C@bu@s*B{@URU%{C%q7>z^i8D_+uBetiUrT? zN?j!MmKaz=VNI+(>{|SruVykVVhG(%WtL+b7i)&?JI|wO>i-2$O9u!c#q)X5PyhfI zAprnTO9KQH00;;O05fZ|)@s)T0y}WCuWwxq0y}V%4t+ubJ8+XyeLn&_aFdgLLJlu* zI9(yd^Lf!w000*uljD6h0yArq8-5%C@RK-xB?3EelV5%u0?BWazi$LFydKi<|el!A!bCW@O7?YQC50lh?5R*fH8Uo#QlRuzlH*e2T`i|)pDldEhrmC;CG9TDvnB%T zRUMKoJw4s)->mCtDUrS7N$&OqTryl^Pgu0rlx|8Q7y{a}^|~hdrflbx`J!YoXKsJC zss`gDrnJQ9FK+scE8e+x-SqWM#j&$fcQo{yl%+MaBg)jqvQkx};@WT5J6ZaRtr_?Y z=YKY<3fNgmY~&dt=G@>sVbQoYSc3!J_eG+AY16PEQ{N=?*{S(=8xLDu`_?u9P}YrY z!6A#Lu~(RmeYc6N8QnxDO^9wj>BNOr9&+=qX0*CqVb3E8re?Q#%5Jb>_*P4>4#iV6 z@&Ygcqmi)d+dX?^x3CK!&$~BdtpE!J*?)Zh_`Bafe(;2C(q9j^dfKoL;hQ?3LD$vs zhN~7ptYjhgVSAM9+h}^3==%x-N&TF+mstHo*9L&fQv;raP~d3-{lHp_1z>zbvhap7 z32YIRJ;HovjP;-EMqog=Q79_OHLi)exwb&nrd6frO0rHSKw1#tQgZg-J~fFDsei~K z8Il+QHuqR2lR+GTG@yjSz*N>Xw>lnjw8xKS=8YHfh3YY~P(U02?zjm7FxekQ21ta! zfB=rV8_%djus0rt%3N_UcNB07le>6QfCVk8mSkXE6uU4sH%j{6Ksxt!0R1o_noXrB zSG83R=4kS2yKLNwTue-z+2S($Wi5KKoWf>0?!P+R|MBC|+w;TIpZ8vWd~;Tb zQEf9^TqDz$N^EPDeH06x*_FCT=q)jIhQb;?`vCv|`~v_06951J0000000000w1K0OqJBUE&vBCxaw3xke<}fElQDl%0=IRO x5po!loqsq233!tcau}2Ee>DOndXo`y7?Xc`2a`pBDgnZiaDXrd0DS-e004v8)#3mE diff --git a/tools/ess-maker-profile/extension/extension.js b/tools/ess-maker-profile/extension/extension.js index 139b293a3..69a244aca 100644 --- a/tools/ess-maker-profile/extension/extension.js +++ b/tools/ess-maker-profile/extension/extension.js @@ -1571,7 +1571,9 @@ function activate(context) { // written by the installer. Legacy values 'lite'/'standard' are // normalized to 'maker'/'developer' in firstInstallDispatch. // Maker mode: applies chat-only layout; user clicks Setup to run /setup. - // Developer mode: injects /setup into Copilot Chat automatically. + // Developer mode: the installer already dispatched /setup before VS Code + // launched, so the extension only records the mode and does not inject + // a second /setup here. // Empty ("") / "prompt": installer left the choice unresolved (e.g. a // maker double-clicked the extension into a stray VS Code window // without running the consolidated installer). firstInstallDispatch From 67bd5dbdb9e21dcccdb1d2f2472ead6581cc62b6 Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Thu, 24 Sep 2026 15:54:21 -0700 Subject: [PATCH 10/11] Fix ReferenceError in startPrereqWatcher blocking activate The push.subscriptions call referenced 'configWatcher', a stale variable name from a removed watcher. The undefined reference threw ReferenceError during activate, which halted execution BEFORE the essMaker.mode read, firstInstallDispatch, and applyChatOnlyLayout. Symptom: extension icon appeared in the activity bar but the chat-first maker layout was never applied, even when settings.json said 'essMaker.mode: maker'. Verified via C:\Users\%APPDATA%\ess-maker-debug.log: only 'activate: ENTRY' lines were present, never the 'alreadyApplied=...' log that comes 60 lines later. Removed the stray configWatcher token. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- .../extension/ess-maker-profile-0.4.28.vsix | Bin 32934 -> 32930 bytes .../ess-maker-profile/extension/extension.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix b/tools/ess-maker-profile/extension/ess-maker-profile-0.4.28.vsix index efebdfcf06c8483fa9177758a54d94d192b61944..9da05f9d47a67988ab952ceb42487ca983da84cb 100644 GIT binary patch delta 4827 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 B%&Y(a delta 4807 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 diff --git a/tools/ess-maker-profile/extension/extension.js b/tools/ess-maker-profile/extension/extension.js index 69a244aca..ee36320d5 100644 --- a/tools/ess-maker-profile/extension/extension.js +++ b/tools/ess-maker-profile/extension/extension.js @@ -220,7 +220,7 @@ function startPrereqWatcher(context) { const interval = setInterval(refresh, 10000); // Register disposables - context.subscriptions.push(stateWatcher, configWatcher, flightcheckWatcher, { + context.subscriptions.push(stateWatcher, flightcheckWatcher, { dispose: () => clearInterval(interval) }); From 6d14bfc1289cbf469a98368732bf469c1be24ee1 Mon Sep 17 00:00:00 2001 From: Avery Milandin Date: Fri, 25 Sep 2026 09:53:06 -0700 Subject: [PATCH 11/11] fix(setup): skip PS 5.1 parse probe on non-Windows before touching SystemRoot The Install-EssAdk.ps1 PS-5.1-parse test guarded the Windows-only path via `Test-Path `, but built `` with `Join-Path C:\Windows ...` first. On Ubuntu CI `C:\Windows` is ``, and Join-Path throws `Cannot bind argument to parameter 'Path' because it is null.` BEFORE the Test-Path guard can fire, failing the smoke test suite. Reorder: check `True` first and bail out on Linux/macOS, then build the Windows PS 5.1 path and probe it. Also switch `` to reuse the module-level `` since it's the same value. Verified locally on Windows: 64/64 tests pass (previously 63/64). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 70ddf006-7f8d-48e7-9afa-3fbae73b3864 --- setup/Install-EssAdk.Tests.ps1 | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/setup/Install-EssAdk.Tests.ps1 b/setup/Install-EssAdk.Tests.ps1 index 99cdfa329..af7d66ff6 100644 --- a/setup/Install-EssAdk.Tests.ps1 +++ b/setup/Install-EssAdk.Tests.ps1 @@ -101,12 +101,19 @@ Test 'Install-EssAdk.ps1 parses under Windows PowerShell 5.1' { # (which is PS7 in this suite) catches nothing; we have to invoke # ``powershell.exe`` (5.1) explicitly with -NoProfile -Command and let # its Language.Parser::ParseFile look at the file. + if (-not $IsWindows) { + # Non-Windows test host: skip (macOS / Linux CI). Windows CI hits + # the parser probe below. Guarding on $IsWindows before touching + # $env:SystemRoot avoids a Join-Path null-Path throw on Linux, + # where SystemRoot does not exist. + return + } $pwsh5 = Join-Path $env:SystemRoot 'System32\WindowsPowerShell\v1.0\powershell.exe' if (-not (Test-Path $pwsh5)) { - # Non-Windows test host: skip (macOS / Linux CI). Windows CI hits this. + # Windows host without inbox PowerShell 5.1 (unusual): skip. return } - $scriptPath = Join-Path $PSScriptRoot 'Install-EssAdk.ps1' + $scriptPath = $installerPath $probe = @' $errors = $null $tokens = $null