Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Structured planning workflow that uses files to track tasks, decisions, and project progress.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
scripts/attest-plan.ps1
1#requires -Version 5.02<#3.SYNOPSIS4Lock the current task_plan.md content with a SHA-256 attestation.56.DESCRIPTION7Use after you finalise (or intentionally edit) a plan. The hooks then refuse8to inject plan content into the model context if the file diverges from the9attested hash, surfacing a "[PLAN TAMPERED]" warning instead.1011Plan resolution:121. $env:PLAN_ID -> ./.planning/$PLAN_ID/132. ./.planning/.active_plan143. Newest ./.planning/<dir>/ by LastWriteTime154. Legacy ./task_plan.md at project root1617.PARAMETER Show18Print the stored hash for the active plan.1920.PARAMETER Clear21Remove the attestation (re-open the plan).22#>23[CmdletBinding(DefaultParameterSetName = "Attest")]24param(25[Parameter(ParameterSetName = "Show")]26[switch] $Show,2728[Parameter(ParameterSetName = "Clear")]29[switch] $Clear30)3132$ErrorActionPreference = "Stop"3334function Resolve-PlanFile {35$planRoot = Join-Path (Get-Location) ".planning"3637if ($env:PLAN_ID) {38$candidate = Join-Path $planRoot $env:PLAN_ID39$planFile = Join-Path $candidate "task_plan.md"40if (Test-Path -LiteralPath $planFile) { return (Resolve-Path -LiteralPath $planFile).Path }41}4243$activePointer = Join-Path $planRoot ".active_plan"44if (Test-Path -LiteralPath $activePointer) {45$planId = (Get-Content -LiteralPath $activePointer -Raw).Trim()46if ($planId) {47$candidate = Join-Path $planRoot $planId48$planFile = Join-Path $candidate "task_plan.md"49if (Test-Path -LiteralPath $planFile) { return (Resolve-Path -LiteralPath $planFile).Path }50}51}5253if (Test-Path -LiteralPath $planRoot) {54$newest = Get-ChildItem -LiteralPath $planRoot -Directory -ErrorAction SilentlyContinue |55Where-Object { -not $_.Name.StartsWith(".") } |56Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName "task_plan.md") } |57Sort-Object LastWriteTime -Descending |58Select-Object -First 159if ($newest) {60return (Resolve-Path -LiteralPath (Join-Path $newest.FullName "task_plan.md")).Path61}62}6364$legacy = Join-Path (Get-Location) "task_plan.md"65if (Test-Path -LiteralPath $legacy) {66return (Resolve-Path -LiteralPath $legacy).Path67}6869return $null70}7172function Get-AttestationPath {73param([string] $PlanFile)74$planDir = Split-Path -Parent $PlanFile75$cwd = (Get-Location).Path76if ($planDir -eq $cwd) {77return (Join-Path $cwd ".plan-attestation")78}79return (Join-Path $planDir ".attestation")80}8182$planFile = Resolve-PlanFile83if (-not $planFile) {84Write-Error "[plan-attest] No task_plan.md found. Create a plan first."85exit 186}8788$attestationFile = Get-AttestationPath -PlanFile $planFile8990if ($Show) {91if (Test-Path -LiteralPath $attestationFile) {92Write-Output "Plan: $planFile"93Write-Output "Attestation: $attestationFile"94Write-Output ("SHA-256: " + (Get-Content -LiteralPath $attestationFile -Raw).Trim())95} else {96Write-Output "[plan-attest] No attestation set for $planFile."97exit 198}99exit 0100}101102if ($Clear) {103if (Test-Path -LiteralPath $attestationFile) {104Remove-Item -LiteralPath $attestationFile -Force105Write-Output "[plan-attest] Cleared attestation for $planFile."106} else {107Write-Output "[plan-attest] No attestation to clear."108}109exit 0110}111112$hashVal = (Get-FileHash -LiteralPath $planFile -Algorithm SHA256).Hash.ToLowerInvariant()113Set-Content -LiteralPath $attestationFile -Value $hashVal -NoNewline -Encoding ascii114$short = $hashVal.Substring(0, 12)115Write-Output "[plan-attest] Locked $planFile"116Write-Output "[plan-attest] SHA-256: $short... (stored in $attestationFile)"117Write-Output "[plan-attest] Hooks will block injection if the file is modified without re-running this command."118exit 0119