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>
96 lines
3.9 KiB
PowerShell
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"
|