planning-with-files 3.10.2 → 3.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "planning-with-files",
3
- "version": "3.10.2",
3
+ "version": "3.11.2",
4
4
  "description": "Persistent file-based planning for AI coding agents: keeps task_plan.md, findings.md and progress.md on disk so the plan survives /clear, context loss and compaction. Manus-style. Ships the skill plus a Pi Coding Agent extension.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -1,137 +1,137 @@
1
- #requires -Version 5.0
2
- <#
3
- .SYNOPSIS
4
- Lock the current task_plan.md content with a SHA-256 attestation.
5
-
6
- .DESCRIPTION
7
- Use after you finalise (or intentionally edit) a plan. The hooks then refuse
8
- to inject plan content into the model context if the file diverges from the
9
- attested hash, surfacing a "[PLAN TAMPERED]" warning instead.
10
-
11
- Plan resolution:
12
- 1. $env:PLAN_ID -> ./.planning/$PLAN_ID/
13
- 2. ./.planning/.active_plan
14
- 3. Newest ./.planning/<dir>/ by LastWriteTime
15
- 4. Legacy ./task_plan.md at project root
16
-
17
- .PARAMETER Show
18
- Print the stored hash for the active plan.
19
-
20
- .PARAMETER Clear
21
- Remove the attestation (re-open the plan).
22
- #>
23
- [CmdletBinding(DefaultParameterSetName = "Attest")]
24
- param(
25
- [Parameter(ParameterSetName = "Show")]
26
- [switch] $Show,
27
-
28
- [Parameter(ParameterSetName = "Clear")]
29
- [switch] $Clear
30
- )
31
-
32
- $ErrorActionPreference = "Stop"
33
-
34
- function Resolve-PlanFile {
35
- $planRoot = Join-Path (Get-Location) ".planning"
36
-
37
- if ($env:PLAN_ID) {
38
- $candidate = Join-Path $planRoot $env:PLAN_ID
39
- $planFile = Join-Path $candidate "task_plan.md"
40
- if (Test-Path -LiteralPath $planFile) { return (Resolve-Path -LiteralPath $planFile).Path }
41
- }
42
-
43
- $activePointer = Join-Path $planRoot ".active_plan"
44
- if (Test-Path -LiteralPath $activePointer) {
45
- $planId = (Get-Content -LiteralPath $activePointer -Raw).Trim()
46
- if ($planId) {
47
- $candidate = Join-Path $planRoot $planId
48
- $planFile = Join-Path $candidate "task_plan.md"
49
- if (Test-Path -LiteralPath $planFile) { return (Resolve-Path -LiteralPath $planFile).Path }
50
- }
51
- }
52
-
53
- if (Test-Path -LiteralPath $planRoot) {
54
- $newest = Get-ChildItem -LiteralPath $planRoot -Directory -ErrorAction SilentlyContinue |
55
- Where-Object { -not $_.Name.StartsWith(".") } |
56
- Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName "task_plan.md") } |
57
- Sort-Object LastWriteTime -Descending |
58
- Select-Object -First 1
59
- if ($newest) {
60
- return (Resolve-Path -LiteralPath (Join-Path $newest.FullName "task_plan.md")).Path
61
- }
62
- }
63
-
64
- $legacy = Join-Path (Get-Location) "task_plan.md"
65
- if (Test-Path -LiteralPath $legacy) {
66
- return (Resolve-Path -LiteralPath $legacy).Path
67
- }
68
-
69
- return $null
70
- }
71
-
72
- function Get-AttestationPath {
73
- param([string] $PlanFile)
74
- $planDir = Split-Path -Parent $PlanFile
75
- $cwd = (Get-Location).Path
76
- if ($planDir -eq $cwd) {
77
- return (Join-Path $cwd ".plan-attestation")
78
- }
79
- return (Join-Path $planDir ".attestation")
80
- }
81
-
82
- $planFile = Resolve-PlanFile
83
- if (-not $planFile) {
84
- Write-Error "[plan-attest] No task_plan.md found. Create a plan first."
85
- exit 1
86
- }
87
-
88
- $attestationFile = Get-AttestationPath -PlanFile $planFile
89
-
90
- if ($Show) {
91
- if (Test-Path -LiteralPath $attestationFile) {
92
- Write-Output "Plan: $planFile"
93
- Write-Output "Attestation: $attestationFile"
94
- Write-Output ("SHA-256: " + (Get-Content -LiteralPath $attestationFile -Raw).Trim())
95
- # Nonce (security A1.4): surface the per-plan nonce if init-session
96
- # generated one next to the attestation. Informational only here; the
97
- # hooks consume it to build collision-proof BEGIN/END delimiters.
98
- $nonceFile = Join-Path (Split-Path -Parent $attestationFile) ".nonce"
99
- if (Test-Path -LiteralPath $nonceFile) {
100
- $nonceVal = (Get-Content -LiteralPath $nonceFile -Raw).Trim()
101
- if ($nonceVal) { Write-Output "Nonce: $nonceVal" }
102
- }
103
- } else {
104
- Write-Output "[plan-attest] No attestation set for $planFile."
105
- exit 1
106
- }
107
- exit 0
108
- }
109
-
110
- if ($Clear) {
111
- if (Test-Path -LiteralPath $attestationFile) {
112
- Remove-Item -LiteralPath $attestationFile -Force
113
- Write-Output "[plan-attest] Cleared attestation for $planFile."
114
- } else {
115
- Write-Output "[plan-attest] No attestation to clear."
116
- }
117
- exit 0
118
- }
119
-
120
- $hashVal = (Get-FileHash -LiteralPath $planFile -Algorithm SHA256).Hash.ToLowerInvariant()
121
- Set-Content -LiteralPath $attestationFile -Value $hashVal -NoNewline -Encoding ascii
122
-
123
- # Integrity verification (security A2.1): confirm the on-disk attestation
124
- # matches the intended hash before reporting success. A silent write failure
125
- # (permissions, full disk) must not leave a stale attestation and exit clean.
126
- $storedHash = (Get-Content -LiteralPath $attestationFile -Raw -ErrorAction SilentlyContinue)
127
- if ($null -ne $storedHash) { $storedHash = $storedHash.Trim() }
128
- if ($storedHash -ne $hashVal) {
129
- Write-Error "[plan-attest] Attestation write verification FAILED for $attestationFile. Expected $hashVal, found $storedHash. The plan is NOT attested."
130
- exit 1
131
- }
132
-
133
- $short = $hashVal.Substring(0, 12)
134
- Write-Output "[plan-attest] Locked $planFile"
135
- Write-Output "[plan-attest] SHA-256: $short... (stored in $attestationFile)"
136
- Write-Output "[plan-attest] Hooks will block injection if the file is modified without re-running this command."
137
- exit 0
1
+ #requires -Version 5.0
2
+ <#
3
+ .SYNOPSIS
4
+ Lock the current task_plan.md content with a SHA-256 attestation.
5
+
6
+ .DESCRIPTION
7
+ Use after you finalise (or intentionally edit) a plan. The hooks then refuse
8
+ to inject plan content into the model context if the file diverges from the
9
+ attested hash, surfacing a "[PLAN TAMPERED]" warning instead.
10
+
11
+ Plan resolution:
12
+ 1. $env:PLAN_ID -> ./.planning/$PLAN_ID/
13
+ 2. ./.planning/.active_plan
14
+ 3. Newest ./.planning/<dir>/ by LastWriteTime
15
+ 4. Legacy ./task_plan.md at project root
16
+
17
+ .PARAMETER Show
18
+ Print the stored hash for the active plan.
19
+
20
+ .PARAMETER Clear
21
+ Remove the attestation (re-open the plan).
22
+ #>
23
+ [CmdletBinding(DefaultParameterSetName = "Attest")]
24
+ param(
25
+ [Parameter(ParameterSetName = "Show")]
26
+ [switch] $Show,
27
+
28
+ [Parameter(ParameterSetName = "Clear")]
29
+ [switch] $Clear
30
+ )
31
+
32
+ $ErrorActionPreference = "Stop"
33
+
34
+ function Resolve-PlanFile {
35
+ $planRoot = Join-Path (Get-Location) ".planning"
36
+
37
+ if ($env:PLAN_ID) {
38
+ $candidate = Join-Path $planRoot $env:PLAN_ID
39
+ $planFile = Join-Path $candidate "task_plan.md"
40
+ if (Test-Path -LiteralPath $planFile) { return (Resolve-Path -LiteralPath $planFile).Path }
41
+ }
42
+
43
+ $activePointer = Join-Path $planRoot ".active_plan"
44
+ if (Test-Path -LiteralPath $activePointer) {
45
+ $planId = (Get-Content -LiteralPath $activePointer -Raw).Trim()
46
+ if ($planId) {
47
+ $candidate = Join-Path $planRoot $planId
48
+ $planFile = Join-Path $candidate "task_plan.md"
49
+ if (Test-Path -LiteralPath $planFile) { return (Resolve-Path -LiteralPath $planFile).Path }
50
+ }
51
+ }
52
+
53
+ if (Test-Path -LiteralPath $planRoot) {
54
+ $newest = Get-ChildItem -LiteralPath $planRoot -Directory -ErrorAction SilentlyContinue |
55
+ Where-Object { -not $_.Name.StartsWith(".") } |
56
+ Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName "task_plan.md") } |
57
+ Sort-Object LastWriteTime -Descending |
58
+ Select-Object -First 1
59
+ if ($newest) {
60
+ return (Resolve-Path -LiteralPath (Join-Path $newest.FullName "task_plan.md")).Path
61
+ }
62
+ }
63
+
64
+ $legacy = Join-Path (Get-Location) "task_plan.md"
65
+ if (Test-Path -LiteralPath $legacy) {
66
+ return (Resolve-Path -LiteralPath $legacy).Path
67
+ }
68
+
69
+ return $null
70
+ }
71
+
72
+ function Get-AttestationPath {
73
+ param([string] $PlanFile)
74
+ $planDir = Split-Path -Parent $PlanFile
75
+ $cwd = (Get-Location).Path
76
+ if ($planDir -eq $cwd) {
77
+ return (Join-Path $cwd ".plan-attestation")
78
+ }
79
+ return (Join-Path $planDir ".attestation")
80
+ }
81
+
82
+ $planFile = Resolve-PlanFile
83
+ if (-not $planFile) {
84
+ Write-Error "[plan-attest] No task_plan.md found. Create a plan first."
85
+ exit 1
86
+ }
87
+
88
+ $attestationFile = Get-AttestationPath -PlanFile $planFile
89
+
90
+ if ($Show) {
91
+ if (Test-Path -LiteralPath $attestationFile) {
92
+ Write-Output "Plan: $planFile"
93
+ Write-Output "Attestation: $attestationFile"
94
+ Write-Output ("SHA-256: " + (Get-Content -LiteralPath $attestationFile -Raw).Trim())
95
+ # Nonce (security A1.4): surface the per-plan nonce if init-session
96
+ # generated one next to the attestation. Informational only here; the
97
+ # hooks consume it to build collision-proof BEGIN/END delimiters.
98
+ $nonceFile = Join-Path (Split-Path -Parent $attestationFile) ".nonce"
99
+ if (Test-Path -LiteralPath $nonceFile) {
100
+ $nonceVal = (Get-Content -LiteralPath $nonceFile -Raw).Trim()
101
+ if ($nonceVal) { Write-Output "Nonce: $nonceVal" }
102
+ }
103
+ } else {
104
+ Write-Output "[plan-attest] No attestation set for $planFile."
105
+ exit 1
106
+ }
107
+ exit 0
108
+ }
109
+
110
+ if ($Clear) {
111
+ if (Test-Path -LiteralPath $attestationFile) {
112
+ Remove-Item -LiteralPath $attestationFile -Force
113
+ Write-Output "[plan-attest] Cleared attestation for $planFile."
114
+ } else {
115
+ Write-Output "[plan-attest] No attestation to clear."
116
+ }
117
+ exit 0
118
+ }
119
+
120
+ $hashVal = (Get-FileHash -LiteralPath $planFile -Algorithm SHA256).Hash.ToLowerInvariant()
121
+ Set-Content -LiteralPath $attestationFile -Value $hashVal -NoNewline -Encoding ascii
122
+
123
+ # Integrity verification (security A2.1): confirm the on-disk attestation
124
+ # matches the intended hash before reporting success. A silent write failure
125
+ # (permissions, full disk) must not leave a stale attestation and exit clean.
126
+ $storedHash = (Get-Content -LiteralPath $attestationFile -Raw -ErrorAction SilentlyContinue)
127
+ if ($null -ne $storedHash) { $storedHash = $storedHash.Trim() }
128
+ if ($storedHash -ne $hashVal) {
129
+ Write-Error "[plan-attest] Attestation write verification FAILED for $attestationFile. Expected $hashVal, found $storedHash. The plan is NOT attested."
130
+ exit 1
131
+ }
132
+
133
+ $short = $hashVal.Substring(0, 12)
134
+ Write-Output "[plan-attest] Locked $planFile"
135
+ Write-Output "[plan-attest] SHA-256: $short... (stored in $attestationFile)"
136
+ Write-Output "[plan-attest] Hooks will block injection if the file is modified without re-running this command."
137
+ exit 0