aw-spec-cli 0.1.28 → 0.1.30
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/assets/.amp/agent.json +22 -0
- package/assets/.amp/settings-edit.json +5 -0
- package/assets/.amp/settings-read.json +12 -0
- package/assets/.appweaver/DESIGN-FORMAT.md +9 -0
- package/assets/.appweaver/STACK-PROFILES.md +102 -15
- package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +40 -18
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +56 -4
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +17 -1
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +12 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +289 -24
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +370 -85
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +349 -65
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +39 -7
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +63 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +5 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +430 -138
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +392 -76
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-lane.orch.yaml +104 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +20 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +26 -3
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +314 -44
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +249 -20
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +17 -2
- package/assets/.appweaver/scripts/python/aw-plangate.py +109 -19
- package/assets/.appweaver/scripts/python/aw-stack.py +1295 -9
- package/assets/.appweaver/templates/delta-design-template.md +7 -5
- package/assets/.appweaver/templates/delta-spec-template.md +2 -0
- package/assets/.appweaver/templates/design-template.md +17 -0
- package/assets/.appweaver/templates/ui-design-template.md +19 -4
- package/assets/.augment/agent.json +22 -0
- package/assets/.augment/commands/appweaver.md +12 -0
- package/assets/.cline/agent.json +22 -0
- package/assets/.clinerules/workflows/appweaver.md +7 -0
- package/assets/.codex/agent.json +22 -0
- package/assets/.continue/agent.json +22 -0
- package/assets/.continue/prompts/appweaver.md +13 -0
- package/assets/.cursor/agent.json +22 -0
- package/assets/.cursor/commands/appweaver.md +7 -0
- package/assets/.devin/agent.json +22 -0
- package/assets/.devin/workflows/appweaver.md +11 -0
- package/assets/.factory/agent.json +22 -0
- package/assets/.factory/commands/appweaver.md +12 -0
- package/assets/.gemini/agent.json +22 -0
- package/assets/.github/prompts/appweaver.prompt.md +13 -0
- package/assets/.kilo/agent.json +28 -0
- package/assets/.kilo/agents/appweaver-edit.md +12 -0
- package/assets/.kilo/agents/appweaver-read.md +12 -0
- package/assets/.kilo/commands/appweaver.md +11 -0
- package/assets/.kiro/agent.json +22 -0
- package/assets/.kiro/skills/appweaver/SKILL.md +12 -0
- package/assets/.opencode/agent.json +22 -0
- package/assets/.opencode/agents/appweaver-edit.md +12 -0
- package/assets/.opencode/agents/appweaver-read.md +13 -0
- package/assets/.opencode/commands/appweaver.md +11 -0
- package/assets/.roo/agent.json +22 -0
- package/assets/.roo/commands/appweaver.md +12 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +14 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +14 -0
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/copy-assets.d.ts +16 -3
- package/dist/utils/copy-assets.d.ts.map +1 -1
- package/dist/utils/copy-assets.js +29 -14
- package/dist/utils/copy-assets.js.map +1 -1
- package/package.json +1 -1
|
@@ -654,8 +654,8 @@ function Invoke-LaneSet {
|
|
|
654
654
|
Invoke-AwStackCore -Op 'set' -Rest $fwd
|
|
655
655
|
}
|
|
656
656
|
|
|
657
|
-
# ─────────────── contract-export /
|
|
658
|
-
# Twins of the bash cmd_contract_export /
|
|
657
|
+
# ─────────────── contract-export / -draft / -combine / -requests / -client / -check ───────────────
|
|
658
|
+
# Twins of the bash cmd_contract_export / _draft / _combine / _requests / _client / _check.
|
|
659
659
|
function Invoke-ContractExport {
|
|
660
660
|
param([string[]]$Rest)
|
|
661
661
|
$fwd = @()
|
|
@@ -668,10 +668,48 @@ function Invoke-ContractExport {
|
|
|
668
668
|
Invoke-AwStackCore -Op 'contract-export' -Rest $fwd
|
|
669
669
|
}
|
|
670
670
|
|
|
671
|
+
function Invoke-ContractDraft {
|
|
672
|
+
param([string[]]$Rest)
|
|
673
|
+
$fwd = @()
|
|
674
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
675
|
+
switch ($Rest[$i]) {
|
|
676
|
+
'--feature-dir' { $v = $Rest[++$i]; if ($v) { $fwd += '--feature-dir'; $fwd += $v } }
|
|
677
|
+
default { Write-Error "contract-draft: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
Invoke-AwStackCore -Op 'contract-draft' -Rest $fwd
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function Invoke-ContractCombine {
|
|
684
|
+
param([string[]]$Rest)
|
|
685
|
+
if ($Rest.Count -gt 0) { Write-Error "contract-combine: unknown arg '$($Rest[0])'"; exit 1 }
|
|
686
|
+
Invoke-AwStackCore -Op 'contract-combine' -Rest @()
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
function Invoke-ContractRequests {
|
|
690
|
+
param([string[]]$Rest)
|
|
691
|
+
$fwd = @()
|
|
692
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
693
|
+
switch ($Rest[$i]) {
|
|
694
|
+
'--feature-dir' { $v = $Rest[++$i]; if ($v) { $fwd += '--feature-dir'; $fwd += $v } }
|
|
695
|
+
default { Write-Error "contract-requests: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
Invoke-AwStackCore -Op 'contract-requests' -Rest $fwd
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
# --doc names the document `{{contract}}` stands for in the lane's `client` command; an
|
|
702
|
+
# empty one is dropped. Twin of bash cmd_contract_client.
|
|
671
703
|
function Invoke-ContractClient {
|
|
672
704
|
param([string[]]$Rest)
|
|
673
|
-
|
|
674
|
-
|
|
705
|
+
$fwd = @('--phase', 'client', '--kind', 'frontend')
|
|
706
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
707
|
+
switch ($Rest[$i]) {
|
|
708
|
+
'--doc' { $v = $Rest[++$i]; if ($v) { $fwd += '--contract'; $fwd += $v } }
|
|
709
|
+
default { Write-Error "contract-client: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
Invoke-AwStackCore -Op 'run-phase' -Rest $fwd
|
|
675
713
|
}
|
|
676
714
|
|
|
677
715
|
function Invoke-ContractCheck {
|
|
@@ -686,6 +724,26 @@ function Invoke-ContractCheck {
|
|
|
686
724
|
Invoke-AwStackCore -Op 'contract-check' -Rest $fwd
|
|
687
725
|
}
|
|
688
726
|
|
|
727
|
+
# cmd_contract_impact: what a change's service delta changes or removes that the frontend
|
|
728
|
+
# master plan binds to. Each path is forwarded only when it is not empty — PowerShell 5.1
|
|
729
|
+
# drops an empty value, and the flag would then take the next one.
|
|
730
|
+
function Invoke-ContractImpact {
|
|
731
|
+
param([string[]]$Rest)
|
|
732
|
+
$fwd = @()
|
|
733
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
734
|
+
switch ($Rest[$i]) {
|
|
735
|
+
{ $_ -in '--delta-backend','--frontend-plan','--delta-frontend' } {
|
|
736
|
+
$flag = $Rest[$i]
|
|
737
|
+
if ($i + 1 -lt $Rest.Count -and -not "$($Rest[$i + 1])".StartsWith('--')) {
|
|
738
|
+
$v = $Rest[++$i]; if ($v) { $fwd += $flag; $fwd += $v }
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
default { Write-Error "contract-impact: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
Invoke-AwStackCore -Op 'contract-impact' -Rest $fwd
|
|
745
|
+
}
|
|
746
|
+
|
|
689
747
|
# The gate: is the declaration USABLE for the phase about to run. Twin of bash
|
|
690
748
|
# cmd_lane_verify. --require names a lane kind the phase cannot run without;
|
|
691
749
|
# --allow-missing-root is the greenfield state, where a lane root folder is declared
|
|
@@ -705,12 +763,31 @@ function Invoke-LaneVerify {
|
|
|
705
763
|
# --flag=value: --command carries a phase name that starts with '/'.
|
|
706
764
|
'--command' { $fwd += ('--command=' + $Rest[++$i]) }
|
|
707
765
|
'--allow-missing-root' { $fwd += '--allow-missing-root' }
|
|
766
|
+
'--working-only' { $fwd += '--working-only' }
|
|
708
767
|
default { Write-Error "lane-verify: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
709
768
|
}
|
|
710
769
|
}
|
|
711
770
|
Invoke-AwStackCore -Op 'verify' -Rest $fwd
|
|
712
771
|
}
|
|
713
772
|
|
|
773
|
+
# The developer's WORKING lane, in .appweaver/lane.json (not committed). Twin of bash
|
|
774
|
+
# cmd_lane_select: no argument reads it and lists the answers the declared stacks offer;
|
|
775
|
+
# --lane writes one of them; --default writes the default for the declared stacks.
|
|
776
|
+
function Invoke-LaneSelect {
|
|
777
|
+
param([string[]]$Rest)
|
|
778
|
+
$fwd = @()
|
|
779
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
780
|
+
switch ($Rest[$i]) {
|
|
781
|
+
# --flag=value: --preselect carries raw user prose, and a value that starts
|
|
782
|
+
# with '-' reads as another flag when passed separately.
|
|
783
|
+
{ $_ -in '--lane','--preselect' } { $fwd += ($Rest[$i] + '=' + $Rest[++$i]) }
|
|
784
|
+
'--default' { $fwd += '--default' }
|
|
785
|
+
default { Write-Error "lane-select: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
Invoke-AwStackCore -Op 'lane-select' -Rest $fwd
|
|
789
|
+
}
|
|
790
|
+
|
|
714
791
|
# Evidence that a lane of this kind exists, from the FILE signals only: a lane in
|
|
715
792
|
# stacks.json, and a stack name appearing in free prose. The authoritative signal
|
|
716
793
|
# — whether the active project's technical map carries `## /aw-spec-<command>` for the
|
|
@@ -2702,11 +2779,14 @@ function Invoke-TestRun {
|
|
|
2702
2779
|
# feature's aggregate name (feature-dir basename) plus any --also referrer
|
|
2703
2780
|
# names — the per-feature integrity scope. .NET only; npm/none ignore it
|
|
2704
2781
|
# and run the full suite. (Kept byte-for-byte in step with the bash cmd_test_run.)
|
|
2705
|
-
|
|
2782
|
+
# --kind runs ONE declared lane's suite — the developer's working lane. Empty means
|
|
2783
|
+
# every declared lane, as before. (Twin of the bash cmd_test_run.)
|
|
2784
|
+
$featureDir = ''; $scope = 'repo'; $also = ''; $kind = ''
|
|
2706
2785
|
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
2707
2786
|
switch ($Rest[$i]) {
|
|
2708
2787
|
{ $_ -in '--feature-dir','-FeatureDir' } { $featureDir = $Rest[++$i] }
|
|
2709
2788
|
{ $_ -in '--scope','-Scope' } { $scope = $Rest[++$i] }
|
|
2789
|
+
{ $_ -in '--kind','-Kind' } { $kind = $Rest[++$i] }
|
|
2710
2790
|
# Repeatable, lists UNION — see the bash twin for why the caller passes two.
|
|
2711
2791
|
{ $_ -in '--also','-Also' } { $v = $Rest[++$i]; $also = if ($also) { "$also,$v" } else { $v } }
|
|
2712
2792
|
}
|
|
@@ -2747,16 +2827,20 @@ function Invoke-TestRun {
|
|
|
2747
2827
|
# VSTest's --filter is the only per-test selector any shipped profile has. Elsewhere a
|
|
2748
2828
|
# slice run delegates and widens to the lane's whole suite, which the fence tolerates
|
|
2749
2829
|
# by design, and reports scoped=false so fence_green comes from `ok`.
|
|
2830
|
+
# A FRONTEND-only run always delegates: the .NET path below runs the service's suite,
|
|
2831
|
+
# which is not the lane being asked about.
|
|
2750
2832
|
# (Kept in step with the bash cmd_test_run.)
|
|
2751
2833
|
$laneCount = Get-LaneField -Field 'lane_count'
|
|
2752
2834
|
$useLanes = $false
|
|
2753
2835
|
if ($laneCount -and $laneCount -ne '0') {
|
|
2754
|
-
if ($scope -ne 'slice' -or -not $dotnetPresent) { $useLanes = $true }
|
|
2836
|
+
if ($scope -ne 'slice' -or -not $dotnetPresent -or $kind -eq 'frontend') { $useLanes = $true }
|
|
2755
2837
|
}
|
|
2756
2838
|
if ($useLanes) {
|
|
2757
2839
|
$py = Get-Py
|
|
2758
2840
|
$core = Join-Path $PSScriptRoot '..' 'python' 'aw-stack.py'
|
|
2759
|
-
$
|
|
2841
|
+
$kindArgs = @()
|
|
2842
|
+
if ($kind) { $kindArgs = @('--kind', $kind) }
|
|
2843
|
+
$line = (& $py $core '--repo' $repo 'run-phase' '--phase' 'test' @kindArgs | Select-Object -First 1)
|
|
2760
2844
|
$d = if ($line) { $line | ConvertFrom-Json } else { $null }
|
|
2761
2845
|
$dLog = if ($d -and $d.log_tail) { [string]$d.log_tail } else { '' }
|
|
2762
2846
|
$dOk = [bool]($d -and $d.ok)
|
|
@@ -3137,9 +3221,35 @@ function Invoke-ModelScaffold {
|
|
|
3137
3221
|
# new` resolves to it.
|
|
3138
3222
|
# `count` and `has_changes` keep counting REAL open changes, so a caller that gates on
|
|
3139
3223
|
# them is unaffected by either flag.
|
|
3224
|
+
#
|
|
3225
|
+
# EACH CHANGE'S LANES, from its delta-spec's `**Lanes**:` line (`lanes[]`, and
|
|
3226
|
+
# `lanes_recorded` false when there is no line, which means every lane). `--lane backend |
|
|
3227
|
+
# frontend | both` lists only the changes that touch one of those lanes; every count, id,
|
|
3228
|
+
# option and preselect above then covers that list alone, and `hidden_ids[]` names the open
|
|
3229
|
+
# changes it left out. No `--lane`, or an empty one, lists every open change. A change that
|
|
3230
|
+
# touches one lane only says so in its option label; the label of one touching both is
|
|
3231
|
+
# unchanged. Twin of bash cmd_cr_list.
|
|
3232
|
+
#
|
|
3233
|
+
# The lanes a change touches, from its delta-spec's **Lanes** line: the kinds it names, or
|
|
3234
|
+
# nothing when the line is missing or still the template placeholder — a change touching
|
|
3235
|
+
# every lane. Callers wrap the call in @() so one kind stays an array. Twin of
|
|
3236
|
+
# read_change_lanes in python/aw-stack.py, which bash cmd_cr_list / cmd_cr_paths load.
|
|
3237
|
+
function Get-ChangeLanes {
|
|
3238
|
+
param([string]$Path)
|
|
3239
|
+
if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return }
|
|
3240
|
+
$t = Get-Content -LiteralPath $Path -Raw -ErrorAction SilentlyContinue
|
|
3241
|
+
if (-not $t) { return }
|
|
3242
|
+
$m = [regex]::Match($t, '(?m)^\*\*Lanes\*\*:[ \t]*(.*?)[ \t]*(?:<!--.*)?$')
|
|
3243
|
+
$v = ''
|
|
3244
|
+
if ($m.Success) { $v = $m.Groups[1].Value.Trim().ToLower() }
|
|
3245
|
+
if (-not $v -or $v.StartsWith('[')) { return }
|
|
3246
|
+
if ($v -match '\bboth\b') { return @('backend', 'frontend') }
|
|
3247
|
+
foreach ($k in @('backend', 'frontend')) { if ($v -match "\b$k\b") { $k } }
|
|
3248
|
+
}
|
|
3249
|
+
|
|
3140
3250
|
function Invoke-CrList {
|
|
3141
3251
|
param([string[]]$Rest)
|
|
3142
|
-
$fdArg = ''; $pre = ''; $incMain = $false; $incNew = $false
|
|
3252
|
+
$fdArg = ''; $pre = ''; $incMain = $false; $incNew = $false; $laneArg = ''
|
|
3143
3253
|
$mainLabel = "main · the feature's own spec — re-plan the master (not a change)"
|
|
3144
3254
|
$newLabel = 'new · open a plan-only change — a refactor or architectural decision, no spec delta'
|
|
3145
3255
|
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
@@ -3148,8 +3258,17 @@ function Invoke-CrList {
|
|
|
3148
3258
|
{ $_ -in '--preselect','-Preselect' } { $pre = $Rest[++$i] }
|
|
3149
3259
|
{ $_ -in '--include-main','-IncludeMain' } { $incMain = $true }
|
|
3150
3260
|
{ $_ -in '--include-new','-IncludeNew' } { $incNew = $true }
|
|
3261
|
+
# PowerShell 5.1 drops an empty value, so the next flag is never taken as one.
|
|
3262
|
+
{ $_ -in '--lane','-Lane' } { if ($i + 1 -lt $Rest.Count -and -not "$($Rest[$i + 1])".StartsWith('-')) { $laneArg = $Rest[++$i] } }
|
|
3151
3263
|
}
|
|
3152
3264
|
}
|
|
3265
|
+
$want = @()
|
|
3266
|
+
foreach ($tok in @([regex]::Split("$laneArg".Trim().ToLower(), '[\s,]+') | Where-Object { $_ })) {
|
|
3267
|
+
if ($tok -eq 'both') { $want += 'backend'; $want += 'frontend' }
|
|
3268
|
+
elseif ($tok -in @('backend', 'frontend')) { $want += $tok }
|
|
3269
|
+
else { Write-Error "cr-list: --lane takes backend, frontend or both, not '$tok'"; exit 1 }
|
|
3270
|
+
}
|
|
3271
|
+
$want = @(@('backend', 'frontend') | Where-Object { $want -contains $_ })
|
|
3153
3272
|
# The WHOLE argument, kept alongside the token. A cr id is matched on the token
|
|
3154
3273
|
# because a selection label starts with one; the synthetic words are matched on the
|
|
3155
3274
|
# whole string so that guidance prose like "new caching layer" — which plan passes
|
|
@@ -3188,6 +3307,10 @@ function Invoke-CrList {
|
|
|
3188
3307
|
$first = (Get-Content $dspec -TotalCount 1 -ErrorAction SilentlyContinue)
|
|
3189
3308
|
if ("$first" -match '^#\s+.*?:\s*(.+)$') { $t = $Matches[1].Trim(); if ($t -and -not $t.StartsWith('[')) { $title = $t } }
|
|
3190
3309
|
}
|
|
3310
|
+
$recorded = @(Get-ChangeLanes -Path $dspec)
|
|
3311
|
+
# Assigned, not $( )-wrapped: a subexpression unrolls a one-kind array to a string.
|
|
3312
|
+
$rowLanes = @('backend', 'frontend')
|
|
3313
|
+
if ($recorded.Count) { $rowLanes = @($recorded) }
|
|
3191
3314
|
$rows += [ordered]@{
|
|
3192
3315
|
cr_id = $d.Name; cr_dir = (ConvertTo-RepoRelative -Path $d.FullName -RepoRoot $repo); wave = $wave
|
|
3193
3316
|
title = $title
|
|
@@ -3198,13 +3321,19 @@ function Invoke-CrList {
|
|
|
3198
3321
|
legacy_delta_design = (Test-Path $legacy)
|
|
3199
3322
|
tracked = ($tracked -contains $d.Name)
|
|
3200
3323
|
active = ($d.Name -eq $active)
|
|
3324
|
+
lanes = $rowLanes
|
|
3325
|
+
lanes_recorded = ($recorded.Count -gt 0)
|
|
3201
3326
|
}
|
|
3202
3327
|
}
|
|
3203
3328
|
}
|
|
3204
3329
|
$rows = @($rows | Sort-Object @{ Expression = { $_.wave } }, @{ Expression = { $_.cr_id } })
|
|
3330
|
+
$hidden = @($rows | Where-Object { $want.Count -and -not @($_.lanes | Where-Object { $want -contains $_ }).Count })
|
|
3331
|
+
$hiddenIds = @($hidden | ForEach-Object { $_.cr_id })
|
|
3332
|
+
$rows = @($rows | Where-Object { $hiddenIds -notcontains $_.cr_id })
|
|
3205
3333
|
$crOpts = @(); foreach ($r in $rows) {
|
|
3206
3334
|
$bits = @($r.cr_id, ("wave " + $r.wave))
|
|
3207
3335
|
if ($r.title) { $bits += $r.title } else { $bits += '(delta-spec not authored yet)' }
|
|
3336
|
+
if (@($r.lanes).Count -eq 1) { $bits += ('{0} only' -f @($r.lanes)[0]) }
|
|
3208
3337
|
$planned = @(); if ($r.delta_backend_plan_exists) { $planned += 'backend' }; if ($r.delta_frontend_plan_exists) { $planned += 'frontend' }
|
|
3209
3338
|
if ($planned.Count) { $bits += ('planned: ' + ($planned -join '+')) }
|
|
3210
3339
|
$crOpts += ($bits -join ' · ')
|
|
@@ -3241,6 +3370,12 @@ function Invoke-CrList {
|
|
|
3241
3370
|
active_change = $active; tracked = @($tracked)
|
|
3242
3371
|
has_changes = ($rows.Count -gt 0)
|
|
3243
3372
|
only_id = $(if ($rows.Count -eq 1) { $rows[0].cr_id } else { '' })
|
|
3373
|
+
# What --lane left out: open changes that touch none of the lanes asked for.
|
|
3374
|
+
lane_filter = @($want)
|
|
3375
|
+
hidden_ids = @($hiddenIds); hidden_count = $hidden.Count
|
|
3376
|
+
hidden_ids_csv = (@($hidden | ForEach-Object { '{0} ({1} only)' -f $_.cr_id, (@($_.lanes) -join '+') }) -join ', ')
|
|
3377
|
+
# A --preselect naming one of those, so a caller can say why it was not selected.
|
|
3378
|
+
preselect_hidden = $(if ($pre -and -not $matched -and $hiddenIds -contains $pre) { $pre } else { '' })
|
|
3244
3379
|
})
|
|
3245
3380
|
}
|
|
3246
3381
|
|
|
@@ -3271,6 +3406,9 @@ function Invoke-CrPaths {
|
|
|
3271
3406
|
$fe = Join-Path $crDir 'delta-frontend-plan.md'
|
|
3272
3407
|
$master = Join-Path $ctx 'domain-model.md'
|
|
3273
3408
|
$wave = [int](Get-WaveOf -FeatureDir $fd -CrId $crId -Repo $repo)
|
|
3409
|
+
$recorded = @(Get-ChangeLanes -Path $dspec)
|
|
3410
|
+
$lanes = @('backend', 'frontend')
|
|
3411
|
+
if ($recorded.Count) { $lanes = @($recorded) }
|
|
3274
3412
|
$pred = @(); $sib = @(); $predRows = @(); $sibRows = @(); $predSpecs = @()
|
|
3275
3413
|
foreach ($f in @(Get-ChildItem -LiteralPath $ctx -Filter 'domain-model.*.delta.md' -File -ErrorAction SilentlyContinue | Sort-Object Name)) {
|
|
3276
3414
|
$other = $f.Name.Substring('domain-model.'.Length)
|
|
@@ -3308,6 +3446,10 @@ function Invoke-CrPaths {
|
|
|
3308
3446
|
delta_backend_plan = (ConvertTo-RepoRelative -Path $be -RepoRoot $repo); delta_backend_plan_exists = (Test-Path $be)
|
|
3309
3447
|
delta_frontend_plan = (ConvertTo-RepoRelative -Path $fe -RepoRoot $repo); delta_frontend_plan_exists = (Test-Path $fe)
|
|
3310
3448
|
wave = $wave
|
|
3449
|
+
# The lanes this change touches, from its delta-spec's **Lanes** line — every lane
|
|
3450
|
+
# when it records none (lanes_recorded false).
|
|
3451
|
+
lanes = @($lanes); lanes_csv = (@($lanes) -join ','); lanes_recorded = ($recorded.Count -gt 0)
|
|
3452
|
+
touches_backend = (@($lanes) -contains 'backend'); touches_frontend = (@($lanes) -contains 'frontend')
|
|
3311
3453
|
context_dir = (ConvertTo-RepoRelative -Path $ctx -RepoRoot $repo)
|
|
3312
3454
|
domain_model = (ConvertTo-RepoRelative -Path $master -RepoRoot $repo); domain_model_exists = (Test-Path $master)
|
|
3313
3455
|
# Lower wave = committed to landing first, so this delta may plan on top of it.
|
|
@@ -3497,6 +3639,62 @@ function Invoke-CrWave {
|
|
|
3497
3639
|
Write-Json ([ordered]@{ cr_id = $crId; wave = [int]$wave; stamped = $stamped; delta_spec = $dspec; delta_model = $dmodel })
|
|
3498
3640
|
}
|
|
3499
3641
|
|
|
3642
|
+
# cmd_cr_lanes: the lanes a change touches, on its delta-spec's **Lanes** line. No --cr-id
|
|
3643
|
+
# lists the answers; with it, reads the line, and --set / --stamp write it. An empty value
|
|
3644
|
+
# is dropped by PowerShell 5.1 before it gets here, so --set goes last in a caller's args
|
|
3645
|
+
# and an answer is forwarded only when there is one.
|
|
3646
|
+
function Invoke-CrLanes {
|
|
3647
|
+
param([string[]]$Rest)
|
|
3648
|
+
$crId = ''; $fdArg = ''; $answer = ''; $stamp = $false
|
|
3649
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
3650
|
+
switch ($Rest[$i]) {
|
|
3651
|
+
'--cr-id' { $crId = $Rest[++$i] }
|
|
3652
|
+
'--feature-dir' { $fdArg = $Rest[++$i] }
|
|
3653
|
+
'--set' { $answer = $Rest[++$i] }
|
|
3654
|
+
'--stamp' { $stamp = $true }
|
|
3655
|
+
default { Write-Error "cr-lanes: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
$crId = Get-CrIdToken -Raw $crId
|
|
3659
|
+
$fwd = @()
|
|
3660
|
+
if ($crId) {
|
|
3661
|
+
$repo = Get-RepoRoot; $fd = Resolve-FeatureDir -Arg $fdArg -RepoRoot $repo
|
|
3662
|
+
if (-not $fd) { Write-Error 'cr-lanes: no feature set and no --feature-dir'; exit 1 }
|
|
3663
|
+
$fwd += '--delta-spec'; $fwd += (Join-Path $fd "changes/$crId/delta-spec.md")
|
|
3664
|
+
} elseif ($stamp -or $answer) {
|
|
3665
|
+
Write-Error 'cr-lanes: --set and --stamp need --cr-id'; exit 1
|
|
3666
|
+
}
|
|
3667
|
+
if ($stamp) { $fwd += '--stamp' }
|
|
3668
|
+
if ($answer) { $fwd += ('--set=' + $answer) }
|
|
3669
|
+
Invoke-AwStackCore -Op 'cr-lanes' -Rest $fwd
|
|
3670
|
+
}
|
|
3671
|
+
|
|
3672
|
+
# analyze-slice-delta's result for ONE lane, written into the change's directory as
|
|
3673
|
+
# .aw-slice-gate.<lane>.json. Twin of bash cmd_slice_gate_stamp; the rule is in aw-stack.py.
|
|
3674
|
+
function Invoke-SliceGateStamp {
|
|
3675
|
+
param([string[]]$Rest)
|
|
3676
|
+
$crId = ''; $fdArg = ''; $fwd = @()
|
|
3677
|
+
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
3678
|
+
switch ($Rest[$i]) {
|
|
3679
|
+
'--cr-id' { $crId = $Rest[++$i] }
|
|
3680
|
+
'--feature-dir' { $fdArg = $Rest[++$i] }
|
|
3681
|
+
# --flag=value: the findings are a JSON list. PowerShell 5.1 drops an empty
|
|
3682
|
+
# argument, so a value that would be the next flag is read as empty instead.
|
|
3683
|
+
{ $_ -in '--lane','--tests-green','--tests-ran','--requirements-ok','--findings' } {
|
|
3684
|
+
$flag = $Rest[$i]; $v = ''
|
|
3685
|
+
if ($i + 1 -lt $Rest.Count -and -not "$($Rest[$i + 1])".StartsWith('--')) { $v = $Rest[++$i] }
|
|
3686
|
+
$fwd += ($flag + '=' + $v)
|
|
3687
|
+
}
|
|
3688
|
+
default { Write-Error "slice-gate-stamp: unknown arg '$($Rest[$i])'"; exit 1 }
|
|
3689
|
+
}
|
|
3690
|
+
}
|
|
3691
|
+
$crId = Get-CrIdToken -Raw $crId
|
|
3692
|
+
if (-not $crId) { Write-Error 'slice-gate-stamp: --cr-id is required'; exit 1 }
|
|
3693
|
+
$repo = Get-RepoRoot; $fd = Resolve-FeatureDir -Arg $fdArg -RepoRoot $repo
|
|
3694
|
+
if (-not $fd) { Write-Error 'slice-gate-stamp: no feature set and no --feature-dir'; exit 1 }
|
|
3695
|
+
Invoke-AwStackCore -Op 'slice-gate-stamp' -Rest (@('--cr-dir', (Join-Path $fd "changes/$crId"), '--cr-id', $crId) + $fwd)
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3500
3698
|
$PyCrWaveDerive = @'
|
|
3501
3699
|
import glob, json, os, re
|
|
3502
3700
|
|
|
@@ -4443,11 +4641,16 @@ function Invoke-CrRecallCheck {
|
|
|
4443
4641
|
# an untagged gap included — is a plan gap. Unreadable input is one plan gap: a verdict that
|
|
4444
4642
|
# could not be read must not pass.
|
|
4445
4643
|
function Invoke-PlanGaps {
|
|
4644
|
+
# --lanes is the developer's WORKING lanes (csv) — see the bash twin cmd_plan_gaps.
|
|
4446
4645
|
param([string[]]$Rest)
|
|
4447
|
-
$raw = ''
|
|
4646
|
+
$raw = ''; $lanesArg = ''
|
|
4448
4647
|
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
4449
|
-
switch ($Rest[$i]) {
|
|
4648
|
+
switch ($Rest[$i]) {
|
|
4649
|
+
{ $_ -in '--gaps','-Gaps' } { $raw = $Rest[++$i] }
|
|
4650
|
+
{ $_ -in '--lanes','-Lanes' } { $lanesArg = $Rest[++$i] }
|
|
4651
|
+
}
|
|
4450
4652
|
}
|
|
4653
|
+
$lanes = @("$lanesArg".Split(',') | ForEach-Object { $_.Trim().ToLower() } | Where-Object { $_ })
|
|
4451
4654
|
$gaps = @(); $readable = $true
|
|
4452
4655
|
if ("$raw".Trim()) {
|
|
4453
4656
|
try {
|
|
@@ -4455,25 +4658,39 @@ function Invoke-PlanGaps {
|
|
|
4455
4658
|
if ($parsed -is [array]) { $gaps = @($parsed) } else { $readable = $false }
|
|
4456
4659
|
} catch { $readable = $false }
|
|
4457
4660
|
}
|
|
4458
|
-
$plan = 0; $routed = @()
|
|
4661
|
+
$plan = 0; $routed = @(); $other = @()
|
|
4662
|
+
$perPlan = @{ backend = 0; frontend = 0 }
|
|
4663
|
+
$perRouted = @{ backend = @(); frontend = @() }
|
|
4459
4664
|
foreach ($g in $gaps) {
|
|
4460
4665
|
if ($g -is [System.Management.Automation.PSCustomObject]) {
|
|
4461
4666
|
$text = "$($g.gap)".Trim(); $route = "$($g.route)".Trim().ToLower(); $ref = "$($g.spec_ref)".Trim()
|
|
4667
|
+
$lane = "$($g.lane)".Trim().ToLower()
|
|
4462
4668
|
} else {
|
|
4463
|
-
$text = "$g".Trim(); $route = ''; $ref = ''
|
|
4669
|
+
$text = "$g".Trim(); $route = ''; $ref = ''; $lane = ''
|
|
4464
4670
|
}
|
|
4671
|
+
$line = $(if ($ref -and $ref -ne '—') { '{0}: {1}' -f $ref, $text } else { $text })
|
|
4672
|
+
$targets = $(if ($lane -in 'backend','frontend') { @($lane) } else { @('backend','frontend') })
|
|
4465
4673
|
if ($text.ToUpper().StartsWith('LIFT-FAILURE') -or $route -eq 'specify') {
|
|
4466
|
-
$routed += $
|
|
4674
|
+
$routed += $line
|
|
4675
|
+
foreach ($t in $targets) { $perRouted[$t] += $line }
|
|
4676
|
+
} elseif ($lanes.Count -gt 0 -and $lane -in 'backend','frontend' -and $lanes -notcontains $lane) {
|
|
4677
|
+
$other += ('{0}: {1}' -f $lane, $line)
|
|
4467
4678
|
} else {
|
|
4468
4679
|
$plan++
|
|
4680
|
+
foreach ($t in $targets) { $perPlan[$t]++ }
|
|
4469
4681
|
}
|
|
4470
4682
|
}
|
|
4471
|
-
if (-not $readable) { $plan++ }
|
|
4472
|
-
$
|
|
4683
|
+
if (-not $readable) { $plan++; $perPlan['backend']++; $perPlan['frontend']++ }
|
|
4684
|
+
function Get-GapVerdict([int]$p, [int]$r) { if ($p -gt 0) { 'gaps' } elseif ($r -gt 0) { 'routed-gaps' } else { 'conformant' } }
|
|
4473
4685
|
Write-Json ([ordered]@{
|
|
4474
|
-
ok = $true; readable = $readable; verdict = $
|
|
4686
|
+
ok = $true; readable = $readable; verdict = (Get-GapVerdict $plan $routed.Count); plan_ok = ($plan -eq 0)
|
|
4475
4687
|
plan_gaps = $plan; routed_gaps = $routed.Count; routed = @($routed)
|
|
4476
|
-
routed_csv = ($routed -join '; ')
|
|
4688
|
+
routed_csv = ($routed -join '; '); lanes = @($lanes)
|
|
4689
|
+
other_gaps = $other.Count; other = @($other); other_csv = ($other -join '; ')
|
|
4690
|
+
backend_verdict = (Get-GapVerdict $perPlan['backend'] $perRouted['backend'].Count)
|
|
4691
|
+
backend_plan_gaps = $perPlan['backend']; backend_routed = @($perRouted['backend'])
|
|
4692
|
+
frontend_verdict = (Get-GapVerdict $perPlan['frontend'] $perRouted['frontend'].Count)
|
|
4693
|
+
frontend_plan_gaps = $perPlan['frontend']; frontend_routed = @($perRouted['frontend'])
|
|
4477
4694
|
})
|
|
4478
4695
|
}
|
|
4479
4696
|
|
|
@@ -4509,9 +4726,12 @@ function Invoke-PlanGateStamp {
|
|
|
4509
4726
|
|
|
4510
4727
|
function Invoke-PlanGateCheck {
|
|
4511
4728
|
param([string[]]$Rest)
|
|
4512
|
-
$fdArg = ''
|
|
4729
|
+
$fdArg = ''; $lane = ''
|
|
4513
4730
|
for ($i = 0; $i -lt $Rest.Count; $i++) {
|
|
4514
|
-
switch ($Rest[$i]) {
|
|
4731
|
+
switch ($Rest[$i]) {
|
|
4732
|
+
{ $_ -in '--feature-dir','-FeatureDir' } { $fdArg = $Rest[++$i] }
|
|
4733
|
+
{ $_ -in '--lane','-Lane' } { $lane = $Rest[++$i] }
|
|
4734
|
+
}
|
|
4515
4735
|
}
|
|
4516
4736
|
$repo = Get-RepoRoot; $fd = Resolve-FeatureDir -Arg $fdArg -RepoRoot $repo
|
|
4517
4737
|
if (-not $fd) { Write-Error 'plan-gate-check: no feature set and no --feature-dir'; exit 1 }
|
|
@@ -4520,7 +4740,9 @@ function Invoke-PlanGateCheck {
|
|
|
4520
4740
|
if (-not $py) { Write-Error 'plan-gate-check: python3 is required'; exit 1 }
|
|
4521
4741
|
$core = Join-Path $PSScriptRoot '..' 'python' 'aw-plangate.py'
|
|
4522
4742
|
if (-not (Test-Path $core)) { Write-Error "plan-gate-check: core not found at $core"; exit 1 }
|
|
4523
|
-
|
|
4743
|
+
$laneArgs = @()
|
|
4744
|
+
if ($lane) { $laneArgs = @('--lane', $lane) }
|
|
4745
|
+
& $py $core check --feature-dir $fd --context-dir $ctx --root $repo @laneArgs
|
|
4524
4746
|
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }
|
|
4525
4747
|
}
|
|
4526
4748
|
|
|
@@ -4657,9 +4879,14 @@ switch ($sub) {
|
|
|
4657
4879
|
'lane-verify' { Invoke-LaneVerify -Rest $rest }
|
|
4658
4880
|
'lane-probe' { Invoke-LaneProbe -Rest $rest }
|
|
4659
4881
|
'lane-set' { Invoke-LaneSet -Rest $rest }
|
|
4882
|
+
'lane-select' { Invoke-LaneSelect -Rest $rest }
|
|
4660
4883
|
'contract-export' { Invoke-ContractExport -Rest $rest }
|
|
4884
|
+
'contract-draft' { Invoke-ContractDraft -Rest $rest }
|
|
4885
|
+
'contract-combine' { Invoke-ContractCombine -Rest $rest }
|
|
4886
|
+
'contract-requests' { Invoke-ContractRequests -Rest $rest }
|
|
4661
4887
|
'contract-client' { Invoke-ContractClient -Rest $rest }
|
|
4662
4888
|
'contract-check' { Invoke-ContractCheck -Rest $rest }
|
|
4889
|
+
'contract-impact' { Invoke-ContractImpact -Rest $rest }
|
|
4663
4890
|
'list-contexts' { Invoke-ListContexts -Rest $rest }
|
|
4664
4891
|
'set-context' { Invoke-SetContext -Rest $rest }
|
|
4665
4892
|
'list-features' { Invoke-ListFeatures -Rest $rest }
|
|
@@ -4685,6 +4912,8 @@ switch ($sub) {
|
|
|
4685
4912
|
'cr-overlap' { Invoke-CrOverlap -Rest $rest }
|
|
4686
4913
|
'cr-activate' { Invoke-CrActivate -Rest $rest }
|
|
4687
4914
|
'cr-wave' { Invoke-CrWave -Rest $rest }
|
|
4915
|
+
'cr-lanes' { Invoke-CrLanes -Rest $rest }
|
|
4916
|
+
'slice-gate-stamp' { Invoke-SliceGateStamp -Rest $rest }
|
|
4688
4917
|
'cr-wave-derive' { Invoke-CrWaveDerive -Rest $rest }
|
|
4689
4918
|
'cr-rebase' { Invoke-CrRebase -Rest $rest }
|
|
4690
4919
|
'cr-archive' { Invoke-CrArchive -Rest $rest }
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
#
|
|
3
3
|
# Cross-reference (OKF dependency) substrate. Logic lives ONCE in
|
|
4
4
|
# ..\python\aw-xref.py; this wrapper exists only so the orchestrator's
|
|
5
|
-
# execute_script (sh:/ps1: dispatch) can invoke it. Args pass through
|
|
5
|
+
# execute_script (sh:/ps1: dispatch) can invoke it. Args pass through, each
|
|
6
|
+
# flag joined to its value (see below).
|
|
6
7
|
#
|
|
7
8
|
# aw-xref.ps1 record --using X --design <design.md> [--origin ...] [--date YYYY-MM-DD] [--root DIR]
|
|
8
9
|
# aw-xref.ps1 list --aggregate Y [--root DIR] [--bundle knowledge]
|
|
@@ -17,5 +18,19 @@
|
|
|
17
18
|
# then the boundary map, then the feature name itself)
|
|
18
19
|
$ErrorActionPreference = "Stop"
|
|
19
20
|
$core = Join-Path $PSScriptRoot "..\python\aw-xref.py"
|
|
20
|
-
|
|
21
|
+
# Each value travels to python as ONE `--flag=value` argument. Windows PowerShell 5.1, and
|
|
22
|
+
# pwsh before 7.3, drop an empty argument when calling a program, which would leave a flag
|
|
23
|
+
# such as --under with no value or with the next flag as its value. `--under=` keeps the
|
|
24
|
+
# empty value, and the core ignores an empty --under. --merge and --prune take no value.
|
|
25
|
+
$switches = @('--merge', '--prune')
|
|
26
|
+
$fwd = @()
|
|
27
|
+
for ($i = 0; $i -lt $args.Count; $i++) {
|
|
28
|
+
$a = "$($args[$i])"
|
|
29
|
+
if ($a.StartsWith('--') -and -not $a.Contains('=') -and $switches -notcontains $a -and $i + 1 -lt $args.Count) {
|
|
30
|
+
$fwd += ($a + '=' + $args[++$i])
|
|
31
|
+
} else {
|
|
32
|
+
$fwd += $a
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
& python3 $core @fwd
|
|
21
36
|
exit $LASTEXITCODE
|