Files
claudetools/.claude/scripts/register-orphan-detector.ps1
Mike Swanson eed3ece2c7 feat: session recovery toolset (orphan detector + /recover)
Reconstructs session logs from Claude Code transcripts when a session
crashes or is closed before /save. Two entry points:

- /recover <uuid|latest> : manual, Claude-reviewed reconstruction
- detect_orphaned_sessions.py : scheduled scan that auto-builds logs for
  substantive, unsaved, not-yet-recovered transcripts (banner-marked
  RECOVERED-UNVERIFIED), commits them, and posts a #bot-alerts FYI.

recover_session.py is the shared engine: Python extracts the verbatim
command/config/reference timeline; Ollama drafts prose-only narrative.
Machine-local ledger (.claude/state/) prevents reprocessing. Reviewed:
git add scoped to own files, ledger written only after successful push,
per-uuid idempotency, --max cap for unattended runs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 18:33:07 -07:00

96 lines
3.9 KiB
PowerShell

# 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"