From 9b5e862d8abace749c72f561b4fd6aa5d0e6e8c8 Mon Sep 17 00:00:00 2001 From: Julian lechner Date: Wed, 16 Sep 2026 17:37:48 +0200 Subject: [PATCH] feat: persist compact session state --- adapters/claude/config/settings.json | 2 ++ scripts/build.ps1 | 4 +++- scripts/build.sh | 6 ++++++ shared/hooks/scripts/Record-Compact.ps1 | 7 +++++++ shared/hooks/scripts/Show-SessionStatePointer.ps1 | 4 ++++ shared/hooks/scripts/record-compact.sh | 5 +++++ shared/hooks/scripts/show-session-state-pointer.sh | 4 ++++ 7 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 shared/hooks/scripts/Record-Compact.ps1 create mode 100644 shared/hooks/scripts/Show-SessionStatePointer.ps1 create mode 100644 shared/hooks/scripts/record-compact.sh create mode 100644 shared/hooks/scripts/show-session-state-pointer.sh diff --git a/adapters/claude/config/settings.json b/adapters/claude/config/settings.json index 806d9b4..4b5d4cb 100644 --- a/adapters/claude/config/settings.json +++ b/adapters/claude/config/settings.json @@ -19,6 +19,8 @@ "command": "__STATUSLINE_COMMAND__ \"__AI_CONFIG_ROOT__/statusline/__STATUSLINE_SCRIPT__\"" }, "hooks": { + "PreCompact": [{"hooks": [{"type": "command", "command": "__HOOK_COMMAND__ \"__AI_CONFIG_ROOT__/hooks/scripts/__COMPACT_SCRIPT__\"", "timeout": 4}]}], + "SessionStart": [{"hooks": [{"type": "command", "command": "__HOOK_COMMAND__ \"__AI_CONFIG_ROOT__/hooks/scripts/__SESSION_POINTER_SCRIPT__\"", "timeout": 4}]}], "Stop": [ { "hooks": [ diff --git a/scripts/build.ps1 b/scripts/build.ps1 index 1b6f64a..91d80fb 100644 --- a/scripts/build.ps1 +++ b/scripts/build.ps1 @@ -190,7 +190,9 @@ foreach ($platform in @('windows','linux')) { $script = if ($platform -eq 'windows') { 'flashbang.ps1' } else { 'flashbang.sh' } $statusLineScript = if ($platform -eq 'windows') { 'statusline.ps1' } else { 'statusline.sh' } $content = Get-Content -LiteralPath $path -Raw - $content = $content.Replace('__HOOK_COMMAND__', $command).Replace('__WINDOWS_HOOK_COMMAND__', $command).Replace('__HOOK_SCRIPT__', $script).Replace('__WINDOWS_HOOK_SCRIPT__', $script).Replace('__STATUSLINE_COMMAND__', $command).Replace('__STATUSLINE_SCRIPT__', $statusLineScript) + $compactScript = if ($platform -eq 'windows') { 'Record-Compact.ps1' } else { 'record-compact.sh' } + $pointerScript = if ($platform -eq 'windows') { 'Show-SessionStatePointer.ps1' } else { 'show-session-state-pointer.sh' } + $content = $content.Replace('__HOOK_COMMAND__', $command).Replace('__WINDOWS_HOOK_COMMAND__', $command).Replace('__HOOK_SCRIPT__', $script).Replace('__WINDOWS_HOOK_SCRIPT__', $script).Replace('__COMPACT_SCRIPT__', $compactScript).Replace('__SESSION_POINTER_SCRIPT__', $pointerScript).Replace('__STATUSLINE_COMMAND__', $command).Replace('__STATUSLINE_SCRIPT__', $statusLineScript) Set-Content -LiteralPath $path -Value $content -Encoding UTF8 } diff --git a/scripts/build.sh b/scripts/build.sh index ab029f7..55db3a5 100755 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -188,10 +188,14 @@ for client in codex claude; do command=bash script=flashbang.sh statusline_script=statusline.sh + compact_script=record-compact.sh + pointer_script=show-session-state-pointer.sh if [ "$platform" = windows ]; then command='powershell -NoProfile -ExecutionPolicy Bypass -File' script=flashbang.ps1 statusline_script=statusline.ps1 + compact_script=Record-Compact.ps1 + pointer_script=Show-SessionStatePointer.ps1 fi path="$output/$client-$platform/$file" content=$(<"$path") @@ -199,6 +203,8 @@ for client in codex claude; do content=${content//__WINDOWS_HOOK_COMMAND__/$command} content=${content//__HOOK_SCRIPT__/$script} content=${content//__WINDOWS_HOOK_SCRIPT__/$script} + content=${content//__COMPACT_SCRIPT__/$compact_script} + content=${content//__SESSION_POINTER_SCRIPT__/$pointer_script} content=${content//__STATUSLINE_COMMAND__/$command} content=${content//__STATUSLINE_SCRIPT__/$statusline_script} printf '%s\n' "$content" > "$path" diff --git a/shared/hooks/scripts/Record-Compact.ps1 b/shared/hooks/scripts/Record-Compact.ps1 new file mode 100644 index 0000000..6ce6c84 --- /dev/null +++ b/shared/hooks/scripts/Record-Compact.ps1 @@ -0,0 +1,7 @@ +[CmdletBinding()] +param() +$root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent +$stateDir = Join-Path $root '.ai-session' +New-Item $stateDir -ItemType Directory -Force | Out-Null +('{"event":"compact","timestamp":"' + (Get-Date).ToUniversalTime().ToString('o') + '"}') | Add-Content (Join-Path $stateDir 'telemetry.jsonl') +exit 0 diff --git a/shared/hooks/scripts/Show-SessionStatePointer.ps1 b/shared/hooks/scripts/Show-SessionStatePointer.ps1 new file mode 100644 index 0000000..c688e31 --- /dev/null +++ b/shared/hooks/scripts/Show-SessionStatePointer.ps1 @@ -0,0 +1,4 @@ +[CmdletBinding()] +param() +$root = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent +if (Test-Path (Join-Path $root '.ai-session/state.json')) { Write-Output 'Task state is available in .ai-session/state.json; read it only if needed.' } diff --git a/shared/hooks/scripts/record-compact.sh b/shared/hooks/scripts/record-compact.sh new file mode 100644 index 0000000..605faf1 --- /dev/null +++ b/shared/hooks/scripts/record-compact.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash +set -euo pipefail +root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd) +mkdir -p "$root/.ai-session" +printf '{"event":"compact","timestamp":"%s"}\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$root/.ai-session/telemetry.jsonl" diff --git a/shared/hooks/scripts/show-session-state-pointer.sh b/shared/hooks/scripts/show-session-state-pointer.sh new file mode 100644 index 0000000..c8f38e1 --- /dev/null +++ b/shared/hooks/scripts/show-session-state-pointer.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -euo pipefail +root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd) +[ -f "$root/.ai-session/state.json" ] && printf 'Task state is available in .ai-session/state.json; read it only if needed.\n'