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/extensions/planning-with-files/__tests__/plan-anchor.test.ts +228 -228
- package/extensions/planning-with-files/package.json +17 -17
- package/extensions/planning-with-files/runtime.ts +788 -788
- package/package.json +1 -1
- package/scripts/attest-plan.ps1 +137 -137
- package/scripts/attest-plan.sh +206 -206
- package/scripts/check-complete.ps1 +253 -253
- package/scripts/check-complete.sh +253 -253
- package/scripts/gate-stop.sh +32 -32
- package/scripts/ledger-append.ps1 +180 -180
- package/scripts/ledger-append.sh +337 -337
- package/scripts/ledger-summary.ps1 +128 -128
- package/scripts/phase-status.ps1 +175 -175
- package/scripts/phase-status.sh +158 -158
- package/scripts/session-catchup.py +876 -876
- package/scripts/set-active-plan.ps1 +51 -51
- package/scripts/set-active-plan.sh +50 -50
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "planning-with-files",
|
|
3
|
-
"version": "3.
|
|
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",
|
package/scripts/attest-plan.ps1
CHANGED
|
@@ -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
|