auspex: Retiring Span coding-hooks on Windows
Last updated: August 27, 2026
Span's earlier agent, coding-hooks, and its replacement, auspex, both register hooks in the same coding tools. While both are installed, every event is captured twice — once into each product's pipeline. This guide gives you a check-then-uninstall script to remove coding-hooks automatically, but only on devices where auspex has fully taken over.
Do not blanket-uninstall coding-hooks across the fleet. A device where the auspex rollout has not landed — or where it landed but never received its organization credentials — would stop reporting entirely. That is why the script verifies auspex first and exits without doing anything when the checks fail.
What "safe to retire" means
The script removes coding-hooks only when all of the following hold:
- auspex is installed at the machine-wide path (
C:\Program Files\auspex\bin\auspex.exe), i.e. deployed by your MDM rather than self-installed. - auspex reports the managed install mode — it resolved the managed configuration your MDM placed, so it is genuinely under central management.
- The daemon is running and healthy —
auspex statusexits 0, meaning the agent is alive and its own checks pass. - Identity is provisioned — both the organization token and the work email resolve. Without them the device captures locally but never uploads, so retiring the old agent would create a silent gap.
Failing any check, the script exits 0 without changing anything, so it is safe to run on a schedule across a mixed fleet.
Why both check 3 and check 4:
auspex statusexits 0 for a healthy or merely degraded agent and non-zero only when a check is genuinely failing (or the daemon is unreachable). A device that is running fine but has never received its organization token reports a warning, not a failure — so it would pass check 3 on its own. Check 4 is what catches it.
The script
Save as Retire-CodingHooks.ps1 and run it as SYSTEM / administrator (uninstalling a per-machine product requires elevation).
#Requires -Version 5.1
# NOT 'Stop'. `auspex status` prints device facts (install mode, warnings) to STDERR by design, so
# stdout stays clean for --json. Under 'Stop', PowerShell promotes any native-command stderr into a
# terminating NativeCommandError and the script dies on a command that succeeded. Every failure path
# below is checked explicitly, so implicit error handling buys nothing here.
$ErrorActionPreference = 'Continue'
# No Set-StrictMode either: the uninstall hives hold keys with no DisplayName at all, and strict mode
# turns each missing-property read into an error record. The property-existence filter is the guard.
$Auspex = 'C:\Program Files\auspex\bin\auspex.exe'
function Write-Log($m) { Write-Output ("[{0}] {1}" -f (Get-Date -Format s), $m) }
# 1. auspex present at the machine-wide path?
if (-not (Test-Path $Auspex)) { Write-Log 'auspex not installed (machine-wide) — leaving coding-hooks in place.'; exit 0 }
# 2 + 3. Healthy, and under central management?
# ToString() each record: 2>&1 turns stderr lines into ErrorRecords, which render as multi-line
# PowerShell error blocks if left to format themselves. We only want the text.
$status = ((& $Auspex status 2>&1 | ForEach-Object { $_.ToString() }) -join "`n")
$healthy = ($LASTEXITCODE -eq 0)
$managed = $status -match 'install mode:\s*managed'
if (-not $healthy) { Write-Log "auspex status is not healthy — leaving coding-hooks in place. Output: $status"; exit 0 }
if (-not $managed) { Write-Log 'auspex is not in managed install mode — leaving coding-hooks in place.'; exit 0 }
# 4. Identity provisioned (token AND work email both resolve)?
$auth = ((& $Auspex auth show 2>&1 | ForEach-Object { $_.ToString() }) -join "`n")
$hasToken = $auth -match 'token:\s*set'
$hasEmail = $auth -match 'work_email:\s*\S+@\S+'
if (-not ($hasToken -and $hasEmail)) { Write-Log 'auspex has no organization identity yet — leaving coding-hooks in place.'; exit 0 }
Write-Log 'auspex is installed, managed, healthy and provisioned — retiring coding-hooks.'
# Uninstall coding-hooks via its registered uninstaller (per-machine and per-user hives).
$roots = @(
'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
$found = $false
foreach ($entry in Get-ItemProperty $roots -ErrorAction SilentlyContinue |
Where-Object DisplayName |
Where-Object { $_.DisplayName -match '^Span\b.*\bHooks\b|coding.?hooks' }) {
$found = $true
Write-Log ("Uninstalling '{0}' {1}" -f $entry.DisplayName, $entry.DisplayVersion)
if ($entry.PSChildName -match '^\{[0-9A-Fa-f-]{36}\}$') {
Start-Process msiexec.exe -ArgumentList '/x', $entry.PSChildName, '/qn', '/norestart' -Wait
} elseif ($entry.QuietUninstallString) {
Start-Process cmd.exe -ArgumentList '/c', $entry.QuietUninstallString -Wait
} elseif ($entry.UninstallString) {
Start-Process cmd.exe -ArgumentList '/c', "$($entry.UninstallString) /S" -Wait
}
}
if (-not $found) { Write-Log 'No coding-hooks installation found — nothing to remove.' }
Write-Log 'Done.'
exit 0
Adjust before you deploy
-
DisplayNamematch. coding-hooks registers under the product nameSpan IDE Hooks(not "coding-hooks" — the pattern^Span\b.*\bHooks\bis what matches it). auspex itself registers asauspexand contains no "Hooks", so it is never matched. Confirm what your own fleet reports before rolling out, since older builds may differ:Get-ItemProperty HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*, HKLM:\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*, HKCU:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\* -ErrorAction SilentlyContinue | Where-Object DisplayName | Where-Object { $_.DisplayName -match 'span|hook' } | Select-Object DisplayName, DisplayVersion, PSChildName -
Per-user installs. If coding-hooks was installed per-user, the
HKCUentries only exist in that user's hive — a SYSTEM-context run won't see them. Either run the script in the user context as well, or remove the per-user copy in the same pass as your other user-scope cleanup.
Running it on a schedule
Run it periodically, not once: devices get auspex at different times, and a device that fails the checks today should be retried later.
- Intune — add as a Platform script (Devices → Scripts and remediations → Platform scripts), run in system context, 64-bit PowerShell. Intune re-runs a failed script but not a successful one, so for recurring evaluation use a remediation instead: the detection script runs the four checks and reports "remediation needed" only when they all pass, and the remediation script does the uninstall.
- Workspace ONE — a Freestyle workflow or a Product with a script step, scheduled daily.
- Rippling — a scheduled device script in the Windows script library.
Whatever the mechanism, the safety property is the same: on a device that is not ready, the script logs why and exits 0.
Verifying afterwards
On a retired device, auspex should be the only agent registering hooks:
& 'C:\Program Files\auspex\bin\auspex.exe' status
Get-Content "$env:APPDATA\Code\User\settings.json" | Select-String 'hook'
Get-Content "$env:USERPROFILE\.claude\settings.json" | Select-String 'hook'
You should see auspex's entries and no coding-hooks entries. If both appear for a while, that is expected until the user's tools restart — but sustained double entries mean the uninstall did not complete; capture the script's log output and send it to the Span team.