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
|
@@ -1,180 +1,180 @@
|
|
|
1
|
-
#requires -Version 5.0
|
|
2
|
-
<#
|
|
3
|
-
.SYNOPSIS
|
|
4
|
-
Append one structured entry to the run-ledger (PowerShell mirror, v3).
|
|
5
|
-
|
|
6
|
-
.DESCRIPTION
|
|
7
|
-
The run-ledger is the machine layer of progress tracking: an append-only
|
|
8
|
-
JSON-lines file per agent under the active plan dir. Workers append here;
|
|
9
|
-
the orchestrator owns progress.md and task_plan.md. See architecture C3.
|
|
10
|
-
|
|
11
|
-
Plan-dir resolution (matches resolve-plan-dir.ps1):
|
|
12
|
-
1. $env:PLAN_ID -> .\.planning\$PLAN_ID\
|
|
13
|
-
2. .\.planning\.active_plan
|
|
14
|
-
3. Newest .\.planning\<dir>\ by LastWriteTime
|
|
15
|
-
4. Legacy: project root (ledger lands beside .\task_plan.md)
|
|
16
|
-
|
|
17
|
-
Writes ONE JSON line to <plan-dir>\ledger-<agent>.jsonl. tick = 1 + max tick
|
|
18
|
-
across ALL ledger-*.jsonl in the plan dir so concurrent agents share a
|
|
19
|
-
monotonic counter.
|
|
20
|
-
|
|
21
|
-
.PARAMETER Event
|
|
22
|
-
One of: progress phase_complete error gate_block attest note.
|
|
23
|
-
|
|
24
|
-
.PARAMETER Summary
|
|
25
|
-
Free text, truncated to 200 chars, newlines stripped.
|
|
26
|
-
|
|
27
|
-
.PARAMETER Agent
|
|
28
|
-
Ledger owner (default "main"); sanitized to [A-Za-z0-9_-].
|
|
29
|
-
|
|
30
|
-
.PARAMETER Phase
|
|
31
|
-
Phase number/name this entry concerns.
|
|
32
|
-
|
|
33
|
-
.PARAMETER Files
|
|
34
|
-
Comma-separated file list recorded as a JSON array.
|
|
35
|
-
#>
|
|
36
|
-
[CmdletBinding()]
|
|
37
|
-
param(
|
|
38
|
-
[Parameter(Mandatory = $true, Position = 0)]
|
|
39
|
-
[string] $Event,
|
|
40
|
-
|
|
41
|
-
[Parameter(Mandatory = $true, Position = 1)]
|
|
42
|
-
[string] $Summary,
|
|
43
|
-
|
|
44
|
-
[string] $Agent = "main",
|
|
45
|
-
|
|
46
|
-
[string] $Phase = "",
|
|
47
|
-
|
|
48
|
-
[string] $Files = ""
|
|
49
|
-
)
|
|
50
|
-
|
|
51
|
-
$ErrorActionPreference = "Stop"
|
|
52
|
-
|
|
53
|
-
$validEvents = @("progress", "phase_complete", "error", "gate_block", "attest", "note")
|
|
54
|
-
|
|
55
|
-
function Resolve-PlanDir {
|
|
56
|
-
$planRoot = Join-Path (Get-Location) ".planning"
|
|
57
|
-
|
|
58
|
-
if ($env:PLAN_ID) {
|
|
59
|
-
$candidate = Join-Path $planRoot $env:PLAN_ID
|
|
60
|
-
if (Test-Path -LiteralPath $candidate -PathType Container) { return $candidate }
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
$activePointer = Join-Path $planRoot ".active_plan"
|
|
64
|
-
if (Test-Path -LiteralPath $activePointer) {
|
|
65
|
-
$planId = (Get-Content -LiteralPath $activePointer -Raw).Trim()
|
|
66
|
-
if ($planId) {
|
|
67
|
-
$candidate = Join-Path $planRoot $planId
|
|
68
|
-
if (Test-Path -LiteralPath $candidate -PathType Container) { return $candidate }
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (Test-Path -LiteralPath $planRoot -PathType Container) {
|
|
73
|
-
$newest = Get-ChildItem -LiteralPath $planRoot -Directory -ErrorAction SilentlyContinue |
|
|
74
|
-
Where-Object { -not $_.Name.StartsWith(".") } |
|
|
75
|
-
Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName "task_plan.md") } |
|
|
76
|
-
Sort-Object LastWriteTime -Descending |
|
|
77
|
-
Select-Object -First 1
|
|
78
|
-
if ($newest) { return $newest.FullName }
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
# Legacy single-file mode: ledger lives beside .\task_plan.md at root.
|
|
82
|
-
return (Get-Location).Path
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function ConvertTo-JsonString {
|
|
86
|
-
param([string] $Value)
|
|
87
|
-
$sb = New-Object System.Text.StringBuilder
|
|
88
|
-
foreach ($ch in $Value.ToCharArray()) {
|
|
89
|
-
switch ($ch) {
|
|
90
|
-
'"' { [void]$sb.Append('\"') }
|
|
91
|
-
'\' { [void]$sb.Append('\\') }
|
|
92
|
-
"`n" { [void]$sb.Append(' ') }
|
|
93
|
-
"`r" { [void]$sb.Append(' ') }
|
|
94
|
-
"`t" { [void]$sb.Append(' ') }
|
|
95
|
-
default {
|
|
96
|
-
if ([int]$ch -lt 32) {
|
|
97
|
-
[void]$sb.Append(' ')
|
|
98
|
-
} else {
|
|
99
|
-
[void]$sb.Append($ch)
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return $sb.ToString()
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function Get-MaxTick {
|
|
108
|
-
param([string] $Dir)
|
|
109
|
-
$max = 0
|
|
110
|
-
$pattern = '"tick"\s*:\s*(\d+)'
|
|
111
|
-
Get-ChildItem -LiteralPath $Dir -Filter "ledger-*.jsonl" -File -ErrorAction SilentlyContinue | ForEach-Object {
|
|
112
|
-
foreach ($line in (Get-Content -LiteralPath $_.FullName -ErrorAction SilentlyContinue)) {
|
|
113
|
-
$m = [regex]::Match($line, $pattern)
|
|
114
|
-
if ($m.Success) {
|
|
115
|
-
$t = [int]$m.Groups[1].Value
|
|
116
|
-
if ($t -gt $max) { $max = $t }
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return $max
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
# Validate event against the allowlist.
|
|
124
|
-
if ($validEvents -notcontains $Event) {
|
|
125
|
-
Write-Error ("[ledger] invalid event '" + $Event + "' (allowed: " + ($validEvents -join ' ') + ")")
|
|
126
|
-
exit 2
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
# Sanitize agent name to [A-Za-z0-9_-]; empty result falls back to "main".
|
|
130
|
-
$agentClean = ($Agent -replace '[^A-Za-z0-9_-]', '')
|
|
131
|
-
if (-not $agentClean) { $agentClean = "main" }
|
|
132
|
-
|
|
133
|
-
# Truncate summary to the 200-character budget before escaping, matching the
|
|
134
|
-
# sh twin. .NET Substring counts characters, never bytes, so multibyte input
|
|
135
|
-
# cannot be clipped mid-codepoint here and no UTF-8 tail repair is needed.
|
|
136
|
-
if ($Summary.Length -gt 200) { $Summary = $Summary.Substring(0, 200) }
|
|
137
|
-
|
|
138
|
-
$planDir = Resolve-PlanDir
|
|
139
|
-
$ledgerFile = Join-Path $planDir ("ledger-" + $agentClean + ".jsonl")
|
|
140
|
-
$lockFile = Join-Path $planDir ".ledger_lock"
|
|
141
|
-
|
|
142
|
-
$ts = [DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
143
|
-
|
|
144
|
-
# Build the files JSON array from the comma-separated list.
|
|
145
|
-
$filesJson = "[]"
|
|
146
|
-
if ($Files) {
|
|
147
|
-
$parts = $Files.Split(",") | Where-Object { $_ -ne "" }
|
|
148
|
-
$escaped = $parts | ForEach-Object { '"' + (ConvertTo-JsonString $_) + '"' }
|
|
149
|
-
$filesJson = "[" + ($escaped -join ",") + "]"
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
$summaryEsc = ConvertTo-JsonString $Summary
|
|
153
|
-
$phaseEsc = ConvertTo-JsonString $Phase
|
|
154
|
-
|
|
155
|
-
# Acquire an exclusive lock on a sidecar so concurrent appenders do not pick
|
|
156
|
-
# the same tick number, then compute tick and append inside the locked window.
|
|
157
|
-
# Atomic append of a single <4KB line is the real guarantee; the lock just
|
|
158
|
-
# serializes the read-tick / write-line pair.
|
|
159
|
-
$fs = $null
|
|
160
|
-
$acquired = $false
|
|
161
|
-
for ($i = 0; $i -lt 50 -and -not $acquired; $i++) {
|
|
162
|
-
try {
|
|
163
|
-
$fs = [System.IO.File]::Open($lockFile, [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
|
|
164
|
-
$acquired = $true
|
|
165
|
-
} catch {
|
|
166
|
-
Start-Sleep -Milliseconds 100
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
try {
|
|
171
|
-
$tick = (Get-MaxTick $planDir) + 1
|
|
172
|
-
$line = '{"tick":' + $tick + ',"ts":"' + $ts + '","agent":"' + $agentClean + '","phase":"' + $phaseEsc + '","event":"' + $Event + '","summary":"' + $summaryEsc + '","files":' + $filesJson + '}'
|
|
173
|
-
Add-Content -LiteralPath $ledgerFile -Value $line -Encoding utf8
|
|
174
|
-
} finally {
|
|
175
|
-
if ($fs) { $fs.Close(); $fs.Dispose() }
|
|
176
|
-
if (Test-Path -LiteralPath $lockFile) { Remove-Item -LiteralPath $lockFile -Force -ErrorAction SilentlyContinue }
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
Write-Output ("[ledger] tick " + $tick + " -> " + $ledgerFile + " (event=" + $Event + " agent=" + $agentClean + ")")
|
|
180
|
-
exit 0
|
|
1
|
+
#requires -Version 5.0
|
|
2
|
+
<#
|
|
3
|
+
.SYNOPSIS
|
|
4
|
+
Append one structured entry to the run-ledger (PowerShell mirror, v3).
|
|
5
|
+
|
|
6
|
+
.DESCRIPTION
|
|
7
|
+
The run-ledger is the machine layer of progress tracking: an append-only
|
|
8
|
+
JSON-lines file per agent under the active plan dir. Workers append here;
|
|
9
|
+
the orchestrator owns progress.md and task_plan.md. See architecture C3.
|
|
10
|
+
|
|
11
|
+
Plan-dir resolution (matches resolve-plan-dir.ps1):
|
|
12
|
+
1. $env:PLAN_ID -> .\.planning\$PLAN_ID\
|
|
13
|
+
2. .\.planning\.active_plan
|
|
14
|
+
3. Newest .\.planning\<dir>\ by LastWriteTime
|
|
15
|
+
4. Legacy: project root (ledger lands beside .\task_plan.md)
|
|
16
|
+
|
|
17
|
+
Writes ONE JSON line to <plan-dir>\ledger-<agent>.jsonl. tick = 1 + max tick
|
|
18
|
+
across ALL ledger-*.jsonl in the plan dir so concurrent agents share a
|
|
19
|
+
monotonic counter.
|
|
20
|
+
|
|
21
|
+
.PARAMETER Event
|
|
22
|
+
One of: progress phase_complete error gate_block attest note.
|
|
23
|
+
|
|
24
|
+
.PARAMETER Summary
|
|
25
|
+
Free text, truncated to 200 chars, newlines stripped.
|
|
26
|
+
|
|
27
|
+
.PARAMETER Agent
|
|
28
|
+
Ledger owner (default "main"); sanitized to [A-Za-z0-9_-].
|
|
29
|
+
|
|
30
|
+
.PARAMETER Phase
|
|
31
|
+
Phase number/name this entry concerns.
|
|
32
|
+
|
|
33
|
+
.PARAMETER Files
|
|
34
|
+
Comma-separated file list recorded as a JSON array.
|
|
35
|
+
#>
|
|
36
|
+
[CmdletBinding()]
|
|
37
|
+
param(
|
|
38
|
+
[Parameter(Mandatory = $true, Position = 0)]
|
|
39
|
+
[string] $Event,
|
|
40
|
+
|
|
41
|
+
[Parameter(Mandatory = $true, Position = 1)]
|
|
42
|
+
[string] $Summary,
|
|
43
|
+
|
|
44
|
+
[string] $Agent = "main",
|
|
45
|
+
|
|
46
|
+
[string] $Phase = "",
|
|
47
|
+
|
|
48
|
+
[string] $Files = ""
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
$ErrorActionPreference = "Stop"
|
|
52
|
+
|
|
53
|
+
$validEvents = @("progress", "phase_complete", "error", "gate_block", "attest", "note")
|
|
54
|
+
|
|
55
|
+
function Resolve-PlanDir {
|
|
56
|
+
$planRoot = Join-Path (Get-Location) ".planning"
|
|
57
|
+
|
|
58
|
+
if ($env:PLAN_ID) {
|
|
59
|
+
$candidate = Join-Path $planRoot $env:PLAN_ID
|
|
60
|
+
if (Test-Path -LiteralPath $candidate -PathType Container) { return $candidate }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
$activePointer = Join-Path $planRoot ".active_plan"
|
|
64
|
+
if (Test-Path -LiteralPath $activePointer) {
|
|
65
|
+
$planId = (Get-Content -LiteralPath $activePointer -Raw).Trim()
|
|
66
|
+
if ($planId) {
|
|
67
|
+
$candidate = Join-Path $planRoot $planId
|
|
68
|
+
if (Test-Path -LiteralPath $candidate -PathType Container) { return $candidate }
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (Test-Path -LiteralPath $planRoot -PathType Container) {
|
|
73
|
+
$newest = Get-ChildItem -LiteralPath $planRoot -Directory -ErrorAction SilentlyContinue |
|
|
74
|
+
Where-Object { -not $_.Name.StartsWith(".") } |
|
|
75
|
+
Where-Object { Test-Path -LiteralPath (Join-Path $_.FullName "task_plan.md") } |
|
|
76
|
+
Sort-Object LastWriteTime -Descending |
|
|
77
|
+
Select-Object -First 1
|
|
78
|
+
if ($newest) { return $newest.FullName }
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
# Legacy single-file mode: ledger lives beside .\task_plan.md at root.
|
|
82
|
+
return (Get-Location).Path
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function ConvertTo-JsonString {
|
|
86
|
+
param([string] $Value)
|
|
87
|
+
$sb = New-Object System.Text.StringBuilder
|
|
88
|
+
foreach ($ch in $Value.ToCharArray()) {
|
|
89
|
+
switch ($ch) {
|
|
90
|
+
'"' { [void]$sb.Append('\"') }
|
|
91
|
+
'\' { [void]$sb.Append('\\') }
|
|
92
|
+
"`n" { [void]$sb.Append(' ') }
|
|
93
|
+
"`r" { [void]$sb.Append(' ') }
|
|
94
|
+
"`t" { [void]$sb.Append(' ') }
|
|
95
|
+
default {
|
|
96
|
+
if ([int]$ch -lt 32) {
|
|
97
|
+
[void]$sb.Append(' ')
|
|
98
|
+
} else {
|
|
99
|
+
[void]$sb.Append($ch)
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return $sb.ToString()
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function Get-MaxTick {
|
|
108
|
+
param([string] $Dir)
|
|
109
|
+
$max = 0
|
|
110
|
+
$pattern = '"tick"\s*:\s*(\d+)'
|
|
111
|
+
Get-ChildItem -LiteralPath $Dir -Filter "ledger-*.jsonl" -File -ErrorAction SilentlyContinue | ForEach-Object {
|
|
112
|
+
foreach ($line in (Get-Content -LiteralPath $_.FullName -ErrorAction SilentlyContinue)) {
|
|
113
|
+
$m = [regex]::Match($line, $pattern)
|
|
114
|
+
if ($m.Success) {
|
|
115
|
+
$t = [int]$m.Groups[1].Value
|
|
116
|
+
if ($t -gt $max) { $max = $t }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return $max
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Validate event against the allowlist.
|
|
124
|
+
if ($validEvents -notcontains $Event) {
|
|
125
|
+
Write-Error ("[ledger] invalid event '" + $Event + "' (allowed: " + ($validEvents -join ' ') + ")")
|
|
126
|
+
exit 2
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Sanitize agent name to [A-Za-z0-9_-]; empty result falls back to "main".
|
|
130
|
+
$agentClean = ($Agent -replace '[^A-Za-z0-9_-]', '')
|
|
131
|
+
if (-not $agentClean) { $agentClean = "main" }
|
|
132
|
+
|
|
133
|
+
# Truncate summary to the 200-character budget before escaping, matching the
|
|
134
|
+
# sh twin. .NET Substring counts characters, never bytes, so multibyte input
|
|
135
|
+
# cannot be clipped mid-codepoint here and no UTF-8 tail repair is needed.
|
|
136
|
+
if ($Summary.Length -gt 200) { $Summary = $Summary.Substring(0, 200) }
|
|
137
|
+
|
|
138
|
+
$planDir = Resolve-PlanDir
|
|
139
|
+
$ledgerFile = Join-Path $planDir ("ledger-" + $agentClean + ".jsonl")
|
|
140
|
+
$lockFile = Join-Path $planDir ".ledger_lock"
|
|
141
|
+
|
|
142
|
+
$ts = [DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
143
|
+
|
|
144
|
+
# Build the files JSON array from the comma-separated list.
|
|
145
|
+
$filesJson = "[]"
|
|
146
|
+
if ($Files) {
|
|
147
|
+
$parts = $Files.Split(",") | Where-Object { $_ -ne "" }
|
|
148
|
+
$escaped = $parts | ForEach-Object { '"' + (ConvertTo-JsonString $_) + '"' }
|
|
149
|
+
$filesJson = "[" + ($escaped -join ",") + "]"
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
$summaryEsc = ConvertTo-JsonString $Summary
|
|
153
|
+
$phaseEsc = ConvertTo-JsonString $Phase
|
|
154
|
+
|
|
155
|
+
# Acquire an exclusive lock on a sidecar so concurrent appenders do not pick
|
|
156
|
+
# the same tick number, then compute tick and append inside the locked window.
|
|
157
|
+
# Atomic append of a single <4KB line is the real guarantee; the lock just
|
|
158
|
+
# serializes the read-tick / write-line pair.
|
|
159
|
+
$fs = $null
|
|
160
|
+
$acquired = $false
|
|
161
|
+
for ($i = 0; $i -lt 50 -and -not $acquired; $i++) {
|
|
162
|
+
try {
|
|
163
|
+
$fs = [System.IO.File]::Open($lockFile, [System.IO.FileMode]::OpenOrCreate, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::None)
|
|
164
|
+
$acquired = $true
|
|
165
|
+
} catch {
|
|
166
|
+
Start-Sleep -Milliseconds 100
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
try {
|
|
171
|
+
$tick = (Get-MaxTick $planDir) + 1
|
|
172
|
+
$line = '{"tick":' + $tick + ',"ts":"' + $ts + '","agent":"' + $agentClean + '","phase":"' + $phaseEsc + '","event":"' + $Event + '","summary":"' + $summaryEsc + '","files":' + $filesJson + '}'
|
|
173
|
+
Add-Content -LiteralPath $ledgerFile -Value $line -Encoding utf8
|
|
174
|
+
} finally {
|
|
175
|
+
if ($fs) { $fs.Close(); $fs.Dispose() }
|
|
176
|
+
if (Test-Path -LiteralPath $lockFile) { Remove-Item -LiteralPath $lockFile -Force -ErrorAction SilentlyContinue }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
Write-Output ("[ledger] tick " + $tick + " -> " + $ledgerFile + " (event=" + $Event + " agent=" + $agentClean + ")")
|
|
180
|
+
exit 0
|