# Changelog sync guard — 统一 stop hook # # 用确定性逻辑检查: # 1. 源文件是否比 changelog 更新(mtime 比较) # 2. stdin 中的 agent 上下文是否显示有源文件编辑操作 # # 静默条件(不触发提醒): # 1. 环境变量 CURSOR_SKIP_CHANGELOG 被设置(sessionStart 在非 agent 模式设置) # 2. stdin JSON 中 composer_mode 不是 "agent"(如 debug/ask/edit 模式) # 3. changelog 文件不存在 # 4. .changelog-ack 标记文件存在且足够新(本会话已确认过 changelog 状态) # 5. 没有源文件比 changelog 更新 # 6. stdin 上下文中没有文件编辑操作的证据(防止跨会话残留 mtime 误触发) $input = [Console]::In.ReadToEnd() # === 豁免检查 1:环境变量跳过标志 === if ($env:CURSOR_SKIP_CHANGELOG) { Write-Output '{}' exit 0 } # === 豁免检查 2:从 stdin 解析 composer_mode === try { $data = $input | ConvertFrom-Json $mode = $data.composer_mode if ($mode -and $mode -ne "agent") { Write-Output '{}' exit 0 } } catch { # JSON 解析失败,继续后续检查 } # === 豁免检查 3:从 stdin 文本匹配 debug 上下文关键词 === # stop hook 的 $ARGUMENTS 可能包含对话/工具上下文,检测 debug 相关信号 if ($input -match '"mode"\s*:\s*"debug"' -or $input -match 'debug[\s_-]?mode' -or $input -match 'Debug Mode') { Write-Output '{}' exit 0 } $changelog = ".cursor\changelog\changelog-headlines.md" $srcDir = "art-agent" # === 豁免检查 4:changelog 文件不存在 === if (-not (Test-Path $changelog)) { Write-Output '{}' exit 0 } $clMtime = (Get-Item $changelog).LastWriteTime # === 豁免检查 5:ack 标记文件足够新 === $ackFile = ".cursor\changelog\.changelog-ack" if ((Test-Path $ackFile) -and (Get-Item $ackFile).LastWriteTime -ge $clMtime) { Write-Output '{}' exit 0 } # === 豁免检查 6:stdin 中无文件编辑证据 === # 防止跨会话残留 mtime 差异导致误触发:如果 ack 文件不存在(或过旧), # 但 stdin 上下文中也没有任何文件写入/编辑操作的痕迹,说明本次会话 # 没有进行代码改动,不应触发提醒。 $hasEditEvidence = ( $input -match 'StrReplace|Write\s*tool|edit_file|file_write|write_to_file' -or $input -match 'Created file|Modified file|Wrote contents' -or $input -match '"tool"\s*:\s*"(str_replace|write|edit)"' ) if (-not $hasEditEvidence) { Write-Output '{}' exit 0 } # === 核心检查:是否有源文件比 changelog 更新 === $extensions = @("*.py", "*.tsx", "*.ts", "*.css") $excludeDirs = @("node_modules", ".next", "__pycache__", "venv") $newerFile = $null foreach ($ext in $extensions) { $files = Get-ChildItem -Path $srcDir -Filter $ext -Recurse -ErrorAction SilentlyContinue | Where-Object { $skip = $false foreach ($ex in $excludeDirs) { if ($_.FullName -like "*\$ex\*") { $skip = $true; break } } -not $skip -and $_.LastWriteTime -gt $clMtime } | Select-Object -First 1 if ($files) { $newerFile = $files.Name break } } if ($newerFile) { $msg = "[Hook] Source file updated (e.g. $newerFile) but changelog not synced. Run dev-changelog Skill operation A NOW to write all three changelog layers." $json = '{"followup_message":"' + $msg.Replace('"','\"') + '"}' Write-Output $json } else { Write-Output '{}' } exit 0