Skip to content

Commit 5f049b5

Browse files
fix: clean up startup menu compatibility (#427 #428)
1 parent 6252404 commit 5f049b5

10 files changed

Lines changed: 121 additions & 28 deletions

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,15 @@ For the first real run:
5050
powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "$releaseRoot\Start-ShareSurfer.ps1" -Force
5151
```
5252

53-
It recursively unblocks ShareSurfer PowerShell files, imports the module, and opens the **ShareSurfer Start Menu**. In a normal PowerShell ConsoleHost it uses arrow-key selection; redirected or locked-down consoles automatically fall back to numbered prompts, and you can force that behavior with `-ConsoleMode Plain`. From the menu you can review readiness, build ownership inputs, start the guided scan setup, validate exports, package the standalone dashboard, and review stop gates. The scan setup asks the first-run questions, asks whether to run intensive share-permission diagnostics before the scan, checks the `inputs` folder for ownership files, offers one ownership-input decision screen, saves `sharesurfer-startup.config.json`, and writes `operator-assistant.plan.json` plus `operator-assistant-rerun.ps1`. In the interactive path it then shows a final review screen, offers to show the generated files, and asks whether to run the generated diagnostic/scan/validate/dashboard script now. The run prompt defaults to No.
53+
It recursively unblocks ShareSurfer PowerShell files, imports the module, and opens the **ShareSurfer Start Menu**. The default menu uses reliable numbered prompts for Windows PowerShell 5.1; operators who want arrow-key selection can opt in with `-ConsoleMode Enhanced`, and redirected or locked-down consoles can still use `-ConsoleMode Plain`. From the menu you can review readiness, build ownership inputs, start the guided scan setup, validate exports, package the standalone dashboard, and review stop gates. The scan setup asks the first-run questions, asks whether to run intensive share-permission diagnostics before the scan, checks the `inputs` folder for ownership files, offers one ownership-input decision screen, saves `sharesurfer-startup.config.json`, and writes `operator-assistant.plan.json` plus `operator-assistant-rerun.ps1`. In the interactive path it then shows a final review screen, offers to show the generated files, and asks whether to run the generated diagnostic/scan/validate/dashboard script now. The run prompt defaults to No.
5454

5555
5. Choose the scan route: UNC path, `-ComputerName` and `-ShareName`, or `-SmbCollectionProvider NativeSmbRpc` when WinRM/CIM is blocked. The startup diagnostic path automatically checks whether a server-returned local path such as `C:\Public\Share` really exists on the collector; when it does not, ShareSurfer attempts the target UNC path instead and records that decision in `share-permission-diagnostics\share_permission_diagnostics.md` and `.csv`.
5656
6. Pick `-ObsAttribute`. The default is `extensionAttribute10`; some labs or smaller AD schemas may need another attribute such as `info`.
5757
7. Run the generated rerun script or `Invoke-ShareSurferScan`, then always run `Test-ShareSurferExport`.
5858
8. Open `report.html`, or package a real export with `scripts\New-ShareSurferStandaloneDashboard.ps1`.
5959
9. Review the stop gates before owner signoff or migration planning.
6060

61-
New operators should start with the [first-run guide](docs/first-run-guide.md) and keep the [command recipes](docs/command-recipes.md) nearby. For a guided console starting point, run `Start-ShareSurfer.ps1` from the release root; it opens the ShareSurfer Start Menu unless you supply `-ConfigPath` for startup replay. After importing the module, advanced operators can run `Start-ShareSurfer` for the same menu or `Start-ShareSurferStartup` to jump directly into startup config generation. The startup flow writes a reusable JSON config and delegates to `Start-ShareSurferOperatorAssistant`; it does not collect data or change permissions until you review and run the generated rerun script. If the arrow-key menu feels rough in an older host, rerun with `-ConsoleMode Plain`. If `owner-mapping.csv` or `ownership-enrichment.csv` is missing, interactive startup first offers to use discovered files and skip missing ones, offers to build missing `ownership-enrichment.csv` from candidate CSVs, or lets you enter advanced custom paths. It can also queue a post-scan `owner-mapping-draft.csv` for the first rerun.
61+
New operators should start with the [first-run guide](docs/first-run-guide.md) and keep the [command recipes](docs/command-recipes.md) nearby. For a guided console starting point, run `Start-ShareSurfer.ps1` from the release root; it opens the ShareSurfer Start Menu unless you supply `-ConfigPath` for startup replay. After importing the module, advanced operators can run `Start-ShareSurfer` for the same menu or `Start-ShareSurferStartup` to jump directly into startup config generation. The startup flow writes a reusable JSON config and delegates to `Start-ShareSurferOperatorAssistant`; it does not collect data or change permissions until you review and run the generated rerun script. The default menu uses numbered prompts; use `-ConsoleMode Enhanced` only when you specifically want arrow-key selection in a console that handles it well. If `owner-mapping.csv` or `ownership-enrichment.csv` is missing, interactive startup first offers to use discovered files and skip missing ones, offers to build missing `ownership-enrichment.csv` from candidate CSVs, or lets you enter advanced custom paths. It can also queue a post-scan `owner-mapping-draft.csv` for the first rerun.
6262

6363
## Pause Before Owner Signoff
6464

Start-ShareSurfer.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ param(
2222
[string] $AclExportMode = 'Compact',
2323

2424
[ValidateSet('Auto', 'Enhanced', 'Plain')]
25-
[string] $ConsoleMode = 'Auto',
25+
[string] $ConsoleMode = 'Plain',
2626

2727
[switch] $Interactive,
2828

docs/command-recipes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ $exportPath = 'C:\ShareSurfer\exports\finance-001'
9393
& "$releaseRoot\Start-ShareSurfer.ps1" -Force
9494
```
9595

96-
The menu shows readiness for ownership inputs, saved startup config, export validation, standalone dashboard packaging, and stop gates. Each menu entry previews the command it will run before asking for confirmation. A normal ConsoleHost uses arrow-key selection; add `-ConsoleMode Plain` when you want simple numbered prompts. If you want to replay an existing startup config without the menu, pass `-ConfigPath` to the same launcher.
96+
The menu shows readiness for ownership inputs, saved startup config, export validation, standalone dashboard packaging, and stop gates. Each menu entry previews the command it will run before asking for confirmation. The default menu uses simple numbered prompts for Windows PowerShell 5.1; add `-ConsoleMode Enhanced` only when you specifically want arrow-key selection in a console that handles it well. If you want to replay an existing startup config without the menu, pass `-ConfigPath` to the same launcher.
9797

9898
If you already know the answers and want to generate the same files without prompts, import the module and call the startup command directly:
9999

docs/first-run-guide.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ Confirm the commands are available:
136136
Get-Command -Module ShareSurfer
137137
```
138138

139-
Recommended: generate a guided startup plan before scanning. This does not collect data or change permissions. It recursively unblocks local ShareSurfer PowerShell files, asks the first-run questions when run interactively, asks whether to run intensive share-permission diagnostics before the scan, checks for optional ownership files in the input folder, writes a reusable startup JSON config, then writes a JSON plan and a rerun script so you can review the requested diagnostic, scan, validation, and standalone dashboard packaging steps first. Optional CSV paths are only used by the rerun script when those files exist. The menu uses arrow-key prompts in a normal ConsoleHost and falls back to numbered prompts when the console does not support raw keys; use `-ConsoleMode Plain` if an older PowerShell 5.1 host feels clunky.
139+
Recommended: generate a guided startup plan before scanning. This does not collect data or change permissions. It recursively unblocks local ShareSurfer PowerShell files, asks the first-run questions when run interactively, asks whether to run intensive share-permission diagnostics before the scan, checks for optional ownership files in the input folder, writes a reusable startup JSON config, then writes a JSON plan and a rerun script so you can review the requested diagnostic, scan, validation, and standalone dashboard packaging steps first. Optional CSV paths are only used by the rerun script when those files exist. The menu defaults to reliable numbered prompts for Windows PowerShell 5.1; use `-ConsoleMode Enhanced` only when you specifically want arrow-key selection in a console that handles it well.
140140

141141
When startup reaches ownership inputs, choose **Use discovered files and skip missing files** for the safest first run. If `ownership-enrichment.csv` is missing, choose **Build ownership enrichment now** to open the multi-CSV ownership import picker when you have HR, employee, OBS, project, application, or owner CSVs that should enrich identities before the scan. Choose **Enter advanced custom paths** only when your CSVs are outside the `inputs` folder. The ownership import writes `ownership-enrichment.csv`, `ownership_context.csv`, `ownership_relationships.csv`, `ownership_import_manifest.csv`, `ownership-import.definition.json`, and `ownership-import-rerun.ps1`, then returns to the normal startup flow.
142142

release-metadata.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"zipAssetName": "ShareSurfer-0.1.0-pre.43.zip",
66
"releaseUrl": "https://github.com/jonathanweinberg/ShareSurfer/releases/tag/v0.1.0-pre.43",
77
"minimumDependencyAgeDays": 7,
8-
"releaseNotesSummary": "Unsigned pre-1.0 startup ACL export mode fix release. v0.1.0-pre.43 includes the v0.1.0-pre.42 export-classification performance work plus a startup/menu fix so guided runs preserve AclExportMode instead of silently falling back to FullEffective. The release-root launcher, Start Menu preview, startup JSON config, operator assistant plan, and generated rerun script now carry Compact or FullEffective consistently into Invoke-ShareSurferScan. Guided startup defaults to Compact for large operator reviews; direct Invoke-ShareSurferScan still uses its own parameter behavior and can be run with -AclExportMode Compact when desired. The package includes the built PowerShell module files, startup launcher, diagnostic scripts, documentation, hashes, release manifest, dependency-age report, and prebuilt standalone dashboard template assets.",
8+
"releaseNotesSummary": "Unsigned pre-1.0 startup ACL export mode fix release. v0.1.0-pre.43 includes the v0.1.0-pre.42 export-classification performance work plus a startup/menu fix so guided runs preserve AclExportMode instead of silently falling back to FullEffective. The release-root launcher, Start Menu preview, startup JSON config, operator assistant plan, and generated rerun script now carry Compact or FullEffective consistently into Invoke-ShareSurferScan. Guided startup defaults to Compact for large operator reviews; direct Invoke-ShareSurferScan still uses its own parameter behavior and can be run with -AclExportMode Compact when desired. This release also accepts AclExportMode as a compatibility no-op for older ownership-import rerun scripts and defaults the Start Menu to reliable numbered prompts while keeping enhanced arrow-key prompts available with -ConsoleMode Enhanced. The package includes the built PowerShell module files, startup launcher, diagnostic scripts, documentation, hashes, release manifest, dependency-age report, and prebuilt standalone dashboard template assets.",
99
"docsReferencePaths": [
1010
"README.md",
1111
"docs/first-run-guide.md",

src/ShareSurfer/Private/ShareSurfer.Console.ps1

Lines changed: 84 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,42 @@
44
# shares one controls contract, renders through one sink, and can be tested
55
# headlessly by driving the state machines with scripted commands.
66
#
7-
# Controls contract (docs/superpowers/specs/2026-07-06-staged-sharesurfer-tui-design.md):
8-
# Enter=accept | arrows/numbers=choose | S=skip | B=back | ?=help | Q=quit
7+
# Controls contract: prompts only advertise the actions enabled by that caller.
98
#
109
# Cancellation contract: prompts and wizards return results whose Action is
1110
# 'Cancelled'; they never throw. Flow entry points decide what cancel means.
1211

1312
function Get-ShareSurferConsoleControlsLine {
14-
'Controls: Enter=accept | arrows/numbers=choose | S=skip | B=back | ?=help | Q=quit'
13+
param(
14+
[switch] $AllowSkip,
15+
16+
[switch] $AllowBack,
17+
18+
[switch] $AllowQuit,
19+
20+
[switch] $AllowCustom,
21+
22+
[switch] $UseArrowKeys
23+
)
24+
25+
$parts = New-Object System.Collections.Generic.List[string]
26+
$parts.Add('Enter=select')
27+
$parts.Add($(if ($UseArrowKeys) { 'arrows/numbers=choose' } else { 'numbers=choose' }))
28+
if ($AllowCustom) {
29+
$parts.Add('type=value')
30+
}
31+
if ($AllowSkip) {
32+
$parts.Add('S=skip')
33+
}
34+
if ($AllowBack) {
35+
$parts.Add('B=back')
36+
}
37+
$parts.Add('?=help')
38+
if ($AllowQuit) {
39+
$parts.Add('Q=quit')
40+
}
41+
42+
'Controls: {0}' -f ($parts -join ' | ')
1543
}
1644

1745
function Test-ShareSurferConsoleRawKeyAvailable {
@@ -242,7 +270,14 @@ function Invoke-ShareSurferConsoleChoiceCommand {
242270

243271
if ($upper -eq '?' -or $upper -eq 'HELP') {
244272
$State.Action = 'Help'
245-
$State.Message = 'Use Up/Down or a number to choose. Enter selects. S skips when available. B goes back when available. Q cancels when available.'
273+
$helpParts = New-Object System.Collections.Generic.List[string]
274+
$helpParts.Add('Use a number to choose. In enhanced console mode, Up/Down also navigates.')
275+
$helpParts.Add('Enter selects.')
276+
if ($AllowCustom) { $helpParts.Add('Type a custom value when the listed choices do not fit.') }
277+
if ($AllowSkip) { $helpParts.Add('S skips this prompt.') }
278+
if ($AllowBack) { $helpParts.Add('B returns to the previous prompt.') }
279+
if ($AllowQuit) { $helpParts.Add('Q cancels this flow.') }
280+
$State.Message = ($helpParts -join ' ')
246281
return $State
247282
}
248283

@@ -251,18 +286,30 @@ function Invoke-ShareSurferConsoleChoiceCommand {
251286
$State.Action = 'Skip'
252287
return $State
253288
}
289+
elseif ($upper -in @('S', 'SKIP')) {
290+
$State.Message = 'Skip is not available on this prompt. Type ? for the available controls.'
291+
return $State
292+
}
254293

255294
if ($upper -in @('B', 'BACK', 'BACKSPACE') -and $AllowBack) {
256295
$State.Done = $true
257296
$State.Action = 'Back'
258297
return $State
259298
}
299+
elseif ($upper -in @('B', 'BACK', 'BACKSPACE')) {
300+
$State.Message = 'Back is not available on this prompt. Use the review/edit choices when this flow offers them.'
301+
return $State
302+
}
260303

261304
if ($upper -in @('Q', 'QUIT', 'ESC', 'ESCAPE') -and $AllowQuit) {
262305
$State.Done = $true
263306
$State.Action = 'Cancelled'
264307
return $State
265308
}
309+
elseif ($upper -in @('Q', 'QUIT', 'ESC', 'ESCAPE')) {
310+
$State.Message = 'Quit is not available on this prompt. Type ? for the available controls.'
311+
return $State
312+
}
266313

267314
if ($text -match '^\d+$') {
268315
$index = [int]$text
@@ -350,7 +397,17 @@ function Get-ShareSurferConsoleChoiceScreen {
350397
[Parameter(Mandatory = $true)]
351398
[string] $Title,
352399

353-
[string] $HelpText = ''
400+
[string] $HelpText = '',
401+
402+
[switch] $AllowSkip,
403+
404+
[switch] $AllowBack,
405+
406+
[switch] $AllowQuit,
407+
408+
[switch] $AllowCustom,
409+
410+
[switch] $UseArrowKeys
354411
)
355412

356413
$lines = New-Object System.Collections.Generic.List[string]
@@ -359,7 +416,7 @@ function Get-ShareSurferConsoleChoiceScreen {
359416
if (-not [string]::IsNullOrWhiteSpace($HelpText)) {
360417
$lines.Add($HelpText)
361418
}
362-
$lines.Add((Get-ShareSurferConsoleControlsLine))
419+
$lines.Add((Get-ShareSurferConsoleControlsLine -AllowSkip:$AllowSkip -AllowBack:$AllowBack -AllowQuit:$AllowQuit -AllowCustom:$AllowCustom -UseArrowKeys:$UseArrowKeys))
363420
$lines.Add('')
364421
for ($index = 0; $index -lt @($State.Options).Count; $index++) {
365422
$option = @($State.Options)[$index]
@@ -385,6 +442,16 @@ function Show-ShareSurferConsoleChoice {
385442

386443
[string] $HelpText = '',
387444

445+
[switch] $AllowSkip,
446+
447+
[switch] $AllowBack,
448+
449+
[switch] $AllowQuit,
450+
451+
[switch] $AllowCustom,
452+
453+
[switch] $UseArrowKeys,
454+
388455
[switch] $ClearBeforeRender
389456
)
390457

@@ -396,7 +463,7 @@ function Show-ShareSurferConsoleChoice {
396463
}
397464
}
398465

399-
Write-ShareSurferConsoleLines -Lines (Get-ShareSurferConsoleChoiceScreen -State $State -Title $Title -HelpText $HelpText)
466+
Write-ShareSurferConsoleLines -Lines (Get-ShareSurferConsoleChoiceScreen -State $State -Title $Title -HelpText $HelpText -AllowSkip:$AllowSkip -AllowBack:$AllowBack -AllowQuit:$AllowQuit -AllowCustom:$AllowCustom -UseArrowKeys:$UseArrowKeys)
400467
}
401468

402469
function Read-ShareSurferConsoleChoice {
@@ -420,7 +487,7 @@ function Read-ShareSurferConsoleChoice {
420487
[switch] $AllowCustom,
421488

422489
[ValidateSet('Auto', 'Enhanced', 'Plain')]
423-
[string] $ConsoleMode = 'Auto',
490+
[string] $ConsoleMode = 'Plain',
424491

425492
$Capabilities = $null
426493
)
@@ -436,7 +503,7 @@ function Read-ShareSurferConsoleChoice {
436503
$renderedOnce = $false
437504
while (-not $state.Done) {
438505
if ($needsRender) {
439-
Show-ShareSurferConsoleChoice -State $state -Title $Title -HelpText $HelpText -ClearBeforeRender:([bool]$renderBehavior.ClearBeforeRender -and $renderedOnce)
506+
Show-ShareSurferConsoleChoice -State $state -Title $Title -HelpText $HelpText -AllowSkip:$AllowSkip -AllowBack:$AllowBack -AllowQuit:$AllowQuit -AllowCustom:$AllowCustom -UseArrowKeys:$useRawKeys -ClearBeforeRender:([bool]$renderBehavior.ClearBeforeRender -and $renderedOnce)
440507
$renderedOnce = $true
441508
$needsRender = $false
442509
}
@@ -524,7 +591,12 @@ function Invoke-ShareSurferConsoleTextCommand {
524591
[string]$State.HelpText
525592
}
526593
else {
527-
'Type a value and press Enter, or press Enter alone to accept the default. S skips when available. B goes back when available. Q cancels when available.'
594+
$textHelpParts = New-Object System.Collections.Generic.List[string]
595+
$textHelpParts.Add('Type a value and press Enter, or press Enter alone to accept the default.')
596+
if ($AllowSkip) { $textHelpParts.Add('S skips this prompt.') }
597+
if ($AllowBack) { $textHelpParts.Add('B returns to the previous prompt.') }
598+
if ($AllowQuit) { $textHelpParts.Add('Q cancels this flow.') }
599+
($textHelpParts -join ' ')
528600
}
529601
return $State
530602
}
@@ -607,7 +679,7 @@ function Read-ShareSurferConsoleBoolean {
607679
[switch] $AllowQuit,
608680

609681
[ValidateSet('Auto', 'Enhanced', 'Plain')]
610-
[string] $ConsoleMode = 'Auto'
682+
[string] $ConsoleMode = 'Plain'
611683
)
612684

613685
$options = @(
@@ -899,7 +971,7 @@ function Show-ShareSurferPromptChoice {
899971
[switch] $AllowQuit
900972
)
901973

902-
Show-ShareSurferConsoleChoice -State $State -Title $Title -HelpText $HelpText
974+
Show-ShareSurferConsoleChoice -State $State -Title $Title -HelpText $HelpText -AllowSkip:$AllowSkip -AllowBack:$AllowBack -AllowQuit:$AllowQuit
903975
}
904976

905977
function Read-ShareSurferPromptChoice {

src/ShareSurfer/Public/Join-ShareSurferOwnershipSources.ps1

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,10 @@ function Join-ShareSurferOwnershipSources {
3535
[ValidateSet('Auto', 'ActiveDirectory', 'Ldap', 'DirectoryOnly')]
3636
[string] $AdLookupMode = 'Auto',
3737

38+
# Compatibility shim for older ownership-import rerun scripts that
39+
# accidentally included this scan-only setting. Ownership import ignores it.
40+
[string] $AclExportMode = '',
41+
3842
[string[]] $ForbiddenOu = @(),
3943

4044
[switch] $Interactive,

0 commit comments

Comments
 (0)