Summary
Every lifecycle event in .github/hooks/shared/telemetry.json declares its powershell command using the ternary operator:
& (Join-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.github' : $env:CLAUDE_PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')
The ternary operator was introduced in PowerShell 7. When the host launches the hook with Windows PowerShell 5.1 (powershell.exe), the command fails to parse before the collector script is ever reached, and the session emits a parser error on every hook event.
Observed error
At line:1 char:69
+ ... Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.githu ...
+ ~
Unexpected token '?' in expression or statement.
At line:1 char:68
+ ... n-Path ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT) ? '.git ...
+ ~
Missing closing ')' in expression.
At line:1 char:162
+ ... _PLUGIN_ROOT) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')
+ ~
Unexpected token ')' in expression or statement.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
Impact
- Telemetry collection silently never runs on affected hosts; the hook dies at parse time.
- Noisy parser errors surface in the session on every lifecycle event (11 events are declared).
- Only affects hosts that route to the
powershell branch with Windows PowerShell 5.1. Hosts using the bash branch or pwsh 7+ are unaffected, which is why this went unnoticed.
Invoke-TelemetryCollector.ps1 itself is already written to be 5.1-safe (it deliberately avoids 3-arg Join-Path and BOM-emitting Add-Content), so the manifest is the only 5.1-incompatible surface. docs/customization/local-telemetry.md also documents the collector as "PowerShell 5.1+", so the manifest contradicts the stated support floor.
Reproduction
Confirmed on Windows PowerShell 5.1.26100.8875:
$cmd = (Get-Content '.github/hooks/shared/telemetry.json' -Raw | ConvertFrom-Json).hooks.sessionStart[0].powershell
powershell.exe -NoProfile -Command "[System.Management.Automation.Language.Parser]::ParseInput('$cmd', [ref]$null, [ref]$null)"
Proposed fix
Replace the ternary with an equivalent if-expression, which parses identically on 5.1 and 7+ and preserves the IsNullOrWhiteSpace fallback semantics:
& (Join-Path $(if ([string]::IsNullOrWhiteSpace($env:CLAUDE_PLUGIN_ROOT)) { '.github' } else { $env:CLAUDE_PLUGIN_ROOT }) 'hooks/shared/telemetry/Invoke-TelemetryCollector.ps1')
Files that must stay in sync:
.github/hooks/shared/telemetry.json — all 11 lifecycle events (sessionStart, userPromptSubmitted, userPromptSubmit, preToolUse, postToolUse, subagentStart, subagentStop, sessionEnd, stop, agentStop, preCompact).
scripts/plugins/Modules/PluginHelpers.psm1 — Write-PluginHookArtifact string-matches the repository form to collapse it to & (Join-Path $env:CLAUDE_PLUGIN_ROOT '...') in materialized plugins. If the manifest changes and this transform does not, installed plugins silently keep the repository fallback.
scripts/tests/plugins/PluginHelpers.Materialization.Tests.ps1 — the Hook plugin root fallback suite asserts the exact repository and installed command strings.
Acceptance criteria
Notes
Existing plugin installations carry the old command and need to be regenerated or reinstalled to pick up the fix.
Summary
Every lifecycle event in
.github/hooks/shared/telemetry.jsondeclares itspowershellcommand using the ternary operator:The ternary operator was introduced in PowerShell 7. When the host launches the hook with Windows PowerShell 5.1 (
powershell.exe), the command fails to parse before the collector script is ever reached, and the session emits a parser error on every hook event.Observed error
Impact
powershellbranch with Windows PowerShell 5.1. Hosts using thebashbranch orpwsh7+ are unaffected, which is why this went unnoticed.Invoke-TelemetryCollector.ps1itself is already written to be 5.1-safe (it deliberately avoids 3-argJoin-Pathand BOM-emittingAdd-Content), so the manifest is the only 5.1-incompatible surface.docs/customization/local-telemetry.mdalso documents the collector as "PowerShell 5.1+", so the manifest contradicts the stated support floor.Reproduction
Confirmed on Windows PowerShell
5.1.26100.8875:Proposed fix
Replace the ternary with an equivalent
if-expression, which parses identically on 5.1 and 7+ and preserves theIsNullOrWhiteSpacefallback semantics:Files that must stay in sync:
.github/hooks/shared/telemetry.json— all 11 lifecycle events (sessionStart,userPromptSubmitted,userPromptSubmit,preToolUse,postToolUse,subagentStart,subagentStop,sessionEnd,stop,agentStop,preCompact).scripts/plugins/Modules/PluginHelpers.psm1—Write-PluginHookArtifactstring-matches the repository form to collapse it to& (Join-Path $env:CLAUDE_PLUGIN_ROOT '...')in materialized plugins. If the manifest changes and this transform does not, installed plugins silently keep the repository fallback.scripts/tests/plugins/PluginHelpers.Materialization.Tests.ps1— theHook plugin root fallbacksuite asserts the exact repository and installed command strings.Acceptance criteria
.github/hooks/shared/telemetry.jsonuses PowerShell 7-only syntax.sessionStartcommand parses without error under Windows PowerShell 5.1.CLAUDE_PLUGIN_ROOTall resolve to.github; an explicit root is passed through.Write-PluginHookArtifactstill produces the installed form, and theHook plugin root fallbacktests pass.npm run lint:jsonandnpm run plugin:validatepass.Notes
Existing plugin installations carry the old command and need to be regenerated or reinstalled to pick up the fix.