# register-orphan-detector.ps1 # Register the "ClaudeTools - Orphaned Session Detector" scheduled task on this # Windows machine. The task runs detect_orphaned_sessions.py, which scans the # per-machine Claude Code transcript directory for unsaved substantive sessions, # auto-builds banner-marked recovery logs, commits + pushes them, and posts an # FYI to #bot-alerts. # # Mirrors the GrepAI watcher registration pattern in .claude/OLLAMA.md. # # Triggers: # - AtLogOn (catch sessions lost since the last logon) # - Daily, repeating every 4 hours (catch crashes during a long workday; # 4h cadence pairs with the detector's 90-minute idle gate so an active # session is never grabbed mid-flight) # # Idempotent: -Force replaces any existing task with the same name. # This script only REGISTERS the task. It does not run the detector now. # # Run from an ordinary (non-admin) PowerShell: # powershell -ExecutionPolicy Bypass -File D:\claudetools\.claude\scripts\register-orphan-detector.ps1 $ErrorActionPreference = "Stop" $TaskName = "ClaudeTools - Orphaned Session Detector" # Resolve the repo root portably. Prefer claudetools_root from identity.json # (per-machine, gitignored); fall back to two levels up from this script # (.claude/scripts/ -> repo root), resolved to a full path. $ScriptDir = $PSScriptRoot $FallbackRoot = (Resolve-Path (Join-Path $ScriptDir "..\..")).Path $IdentityPath = Join-Path $FallbackRoot ".claude\identity.json" $RepoRoot = $FallbackRoot if (Test-Path $IdentityPath) { try { $identity = Get-Content -Raw -Path $IdentityPath | ConvertFrom-Json if ($identity.claudetools_root -and (Test-Path $identity.claudetools_root)) { $RepoRoot = (Resolve-Path $identity.claudetools_root).Path } } catch { Write-Host "[WARNING] Could not parse $IdentityPath; using $FallbackRoot" -ForegroundColor Yellow } } $Script = Join-Path $RepoRoot ".claude\scripts\detect_orphaned_sessions.py" if (-not (Test-Path $Script)) { Write-Host "[ERROR] Detector not found at $Script" -ForegroundColor Red exit 1 } # Resolve the py launcher's full path (the action's Execute wants an absolute # path; "py" alone usually resolves but we pin it for reliability under the # Task Scheduler's environment). $PyCmd = Get-Command py -ErrorAction SilentlyContinue if ($null -ne $PyCmd) { $PyPath = $PyCmd.Source } else { $PyPath = "py" # fall back to PATH resolution at run time } $Action = New-ScheduledTaskAction ` -Execute $PyPath ` -Argument "`"$Script`"" ` -WorkingDirectory $RepoRoot # Trigger 1: at logon for the current user. $TriggerLogon = New-ScheduledTaskTrigger -AtLogOn -User $env:USERNAME # Trigger 2: daily at a fixed start, repeating every 4 hours all day. $TriggerDaily = New-ScheduledTaskTrigger -Daily -At 9am $TriggerDaily.Repetition = (New-ScheduledTaskTrigger ` -Once -At 9am ` -RepetitionInterval (New-TimeSpan -Hours 4) ` -RepetitionDuration (New-TimeSpan -Hours 24)).Repetition $Settings = New-ScheduledTaskSettingsSet ` -ExecutionTimeLimit (New-TimeSpan -Minutes 30) ` -MultipleInstances IgnoreNew ` -StartWhenAvailable ` -DontStopOnIdleEnd Register-ScheduledTask ` -TaskName $TaskName ` -Action $Action ` -Trigger $TriggerLogon, $TriggerDaily ` -Settings $Settings ` -Description "Scans Claude Code transcripts for unsaved substantive sessions and auto-recovers them into session logs." ` -Force | Out-Null Write-Host "[OK] Registered scheduled task '$TaskName'." Write-Host "[INFO] Action: $PyPath `"$Script`"" Write-Host "[INFO] WorkDir: $RepoRoot" Write-Host "[INFO] Triggers: AtLogOn ($env:USERNAME) + daily every 4h" Write-Host "[INFO] To inspect: Get-ScheduledTask -TaskName '$TaskName' | Format-List" Write-Host "[INFO] To run now: Start-ScheduledTask -TaskName '$TaskName'" Write-Host "[INFO] To remove: Unregister-ScheduledTask -TaskName '$TaskName' -Confirm:`$false"