aw-spec-cli 0.1.20 → 0.1.21
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/.appweaver/ORCH-DIALECT.md +8 -3
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +22 -1
- package/assets/.appweaver/orchestrator/orchestrator.py +65 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan-delta.orch.yaml +6 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +107 -48
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +17 -9
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +265 -20
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +89 -15
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +140 -36
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +354 -52
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +47 -23
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +1 -1
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +651 -76
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +600 -59
- package/assets/.appweaver/scripts/python/aw-crstate.py +194 -0
- package/assets/.appweaver/scripts/python/aw-foundation.py +151 -0
- package/assets/.appweaver/scripts/python/aw-payload-parity.py +3 -0
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +423 -0
- package/assets/.appweaver/scripts/python/aw-stack.py +251 -28
- package/assets/.appweaver/scripts/python/aw-testlog.py +148 -0
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +1 -0
- package/assets/.appweaver/stack-profiles/java-spring.json +1 -0
- package/assets/.appweaver/stack-profiles/node-express.json +1 -0
- package/assets/.appweaver/stack-profiles/python-fastapi.json +1 -0
- package/assets/.appweaver/templates/constitution-domain-template.md +17 -2
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +10 -6
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +4 -0
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +3 -1
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +5 -3
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +5 -1
- package/assets/guardrails/technology/dotnet-webapi/domain-driven-design.md +13 -5
- package/assets/guardrails/technology/dotnet-webapi/examples/cqrs.md +2 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/domain-driven-design.md +6 -2
- package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +70 -0
- package/assets/guardrails/technology/dotnet-webapi/technology-constitution.md +3 -0
- package/assets/guardrails/technology/dotnet-webapi/testing.md +10 -0
- package/assets/guardrails/technology/nextjs/ui-constitution.md +8 -1
- package/package.json +1 -1
- package/assets/.appweaver/orchestrator/.state/smoke.state.json +0 -32
|
@@ -12,9 +12,26 @@
|
|
|
12
12
|
# Interactive confirmations (proceed / overwrite / reference sample) ride the
|
|
13
13
|
# checkpoint channel under EVERY strategy.
|
|
14
14
|
#
|
|
15
|
+
# A SCAFFOLD LANDS IN A LANE. The output directory is the SOURCE PATH of the lane being
|
|
16
|
+
# scaffolded, read from .appweaver/stacks.json — never the repository root and never a
|
|
17
|
+
# directory named on the command line. One bundle is one lane's project, so a repo with a
|
|
18
|
+
# service and a UI is scaffolded by running this twice, once per lane, and each run's files
|
|
19
|
+
# land in the subtree that lane owns. That is what makes every later phase's "write only
|
|
20
|
+
# into your lane" rule true of the scaffold as well as of generated code: implement,
|
|
21
|
+
# build, test and run all resolve the same path this unpacked into.
|
|
22
|
+
#
|
|
23
|
+
# The lane's TEST PATH is its second subtree, and for most backend stacks it is a sibling
|
|
24
|
+
# of the source path rather than a directory inside it. A pack carrying its own test
|
|
25
|
+
# project is unpacked like everything else and then has that tree lifted onto the lane's
|
|
26
|
+
# test path — see .appweaver/scripts/python/aw-foundation.py for when that is a no-op.
|
|
27
|
+
#
|
|
28
|
+
# Lanes must therefore be declared FIRST: /appweaver select-stack is the sole writer of
|
|
29
|
+
# stacks.json and the first command of both sequences.
|
|
30
|
+
#
|
|
15
31
|
# Launch via the parametric skill: /appweaver foundation [strategy]
|
|
16
|
-
# Interactive (no args):
|
|
17
|
-
# for the app (project) name —
|
|
32
|
+
# Interactive (no args): ASKS which lane to scaffold, LISTS the foundations available
|
|
33
|
+
# for that lane's ecosystem, then ASKS for the app (project) name — all deterministic,
|
|
34
|
+
# no LLM. Fast path still works:
|
|
18
35
|
# /appweaver foundation agent VerticalSlice AppWeaver.Health
|
|
19
36
|
#
|
|
20
37
|
# Headless:
|
|
@@ -25,14 +42,14 @@
|
|
|
25
42
|
# =============================================================================
|
|
26
43
|
|
|
27
44
|
name: aw-spec-foundation
|
|
28
|
-
description: Scaffold a complete, compiling project — pick from the
|
|
29
|
-
version: 1.
|
|
45
|
+
description: Scaffold a complete, compiling project INTO A DECLARED LANE — pick the lane, pick from the foundation bundles available for its ecosystem, enter the app name, and the verified bundle is unpacked into that lane's source path with its sentinel renamed. Selection is zero-LLM (list + pick + ask).
|
|
46
|
+
version: 1.2.0
|
|
30
47
|
agent_agnostic: true
|
|
31
48
|
dialect: aw-spec.orchestrator/v1
|
|
32
49
|
|
|
33
50
|
inputs:
|
|
34
51
|
user_input:
|
|
35
|
-
description: "Optional fast path: FoundationName [AppName]
|
|
52
|
+
description: "Optional fast path: FoundationName [AppName]. Omit to pick a foundation from the list and be asked for the app name. There is no output-directory argument — the scaffold goes into the chosen lane's source path."
|
|
36
53
|
required: false
|
|
37
54
|
bind: USER_INPUT
|
|
38
55
|
|
|
@@ -50,21 +67,83 @@ vars:
|
|
|
50
67
|
stash_found: "false" # true only when the reference flow stashed a DbContext
|
|
51
68
|
app: "" # resolved app name: fast-path 2nd token, else the ask below
|
|
52
69
|
app_typed: "" # app name entered interactively (empty on the fast path)
|
|
70
|
+
out_ok: "true" # cleared when a third fast-path token names an output directory
|
|
53
71
|
|
|
54
72
|
workflow:
|
|
55
73
|
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
74
|
+
# 0a. THE LANE MODEL, which is where this scaffold is going. Read-only: select-stack is
|
|
75
|
+
# the sole writer, and a scaffold has no business declaring the lane it fills.
|
|
76
|
+
- id: lanes
|
|
77
|
+
action: execute_script
|
|
78
|
+
on_error: continue # never crash raw on an absent stacks.json — the gate explains it
|
|
79
|
+
script: { ref: actions }
|
|
80
|
+
subcommand: lane-resolve
|
|
81
|
+
outputs:
|
|
82
|
+
- { name: lane_options, from: "$.lane_options" }
|
|
83
|
+
- { name: lane_count, from: "$.lane_count" }
|
|
84
|
+
|
|
85
|
+
- id: require_lanes
|
|
86
|
+
action: verify_context
|
|
87
|
+
require_non_empty: lane_options
|
|
88
|
+
on_empty: stop
|
|
89
|
+
on_empty_message: >
|
|
90
|
+
/appweaver foundation cannot run: no lane is declared in .appweaver/stacks.json.
|
|
91
|
+
A scaffold is unpacked into the SOURCE PATH of the lane it belongs to — that is what
|
|
92
|
+
keeps a project's files inside the subtree every later phase compiles, tests and
|
|
93
|
+
generates into — so there has to be a lane to scaffold before there is a scaffold.
|
|
94
|
+
Run /appweaver select-stack first: it declares each lane's stack and source path, and
|
|
95
|
+
is the first command of both the greenfield and the brownfield sequence.
|
|
96
|
+
|
|
97
|
+
# 0b. WHICH lane. One bundle is one lane's project, so a fullstack repo runs this once
|
|
98
|
+
# per lane rather than unpacking a tree that spans both. A single declared lane
|
|
99
|
+
# auto-confirms; the option carries its own explanation and resolves below.
|
|
100
|
+
- id: pick_lane
|
|
101
|
+
action: user_select
|
|
102
|
+
prompt: "Which lane is this scaffold for? The bundle is unpacked into that lane's source path, and its tests into the lane's test path — nothing lands outside them. Scaffold the other lane by running /appweaver foundation again."
|
|
103
|
+
options_from_variable: lane_options
|
|
104
|
+
single_option_confirm: true
|
|
105
|
+
outputs:
|
|
106
|
+
- { name: chosen_lane, from: selection }
|
|
107
|
+
|
|
108
|
+
# 0c. Resolve the chosen lane to the two paths this run writes into, plus the ecosystem
|
|
109
|
+
# that narrows the bundle list. `lane-resolve` strips the option's label itself, so
|
|
110
|
+
# the selection goes back in as the id it already carries.
|
|
111
|
+
- id: lane_target
|
|
112
|
+
action: execute_script
|
|
113
|
+
script: { ref: actions }
|
|
114
|
+
subcommand: lane-resolve
|
|
115
|
+
args: ["--id", "{{chosen_lane}}"]
|
|
116
|
+
outputs:
|
|
117
|
+
- { name: lane_found, from: "$.found" }
|
|
118
|
+
- { name: lane_id, from: "$.lane_id" }
|
|
119
|
+
- { name: lane_root, from: "$.source_path" }
|
|
120
|
+
- { name: lane_test_path, from: "$.test_path" }
|
|
121
|
+
- { name: lane_ecosystem, from: "$.ecosystem" }
|
|
122
|
+
- { name: lane_language, from: "$.language" }
|
|
123
|
+
|
|
124
|
+
- id: lane_resolved
|
|
125
|
+
action: verify_context
|
|
126
|
+
require_true: lane_found
|
|
127
|
+
on_false: stop
|
|
128
|
+
on_false_message: "No lane matches the selection `{{chosen_lane}}` in .appweaver/stacks.json. Re-run /appweaver select-stack to re-declare the lanes, then re-run foundation."
|
|
129
|
+
|
|
130
|
+
- id: lane_has_root
|
|
131
|
+
action: verify_context
|
|
132
|
+
require_non_empty: lane_root
|
|
133
|
+
on_empty: stop
|
|
134
|
+
on_empty_message: "Lane `{{lane_id}}` declares no source path in .appweaver/stacks.json, and the source path is where this scaffold is unpacked. Re-run /appweaver select-stack and give the lane the directory it owns."
|
|
135
|
+
|
|
136
|
+
# 1a. LIST the foundations available for THAT LANE's ecosystem (deterministic
|
|
137
|
+
# foundations/<ecosystem>/<Name>/<Name>.pack.txt scan — no LLM). A bundle is a whole
|
|
138
|
+
# compiling project in one ecosystem's build system, so a Node UI lane is not offered
|
|
139
|
+
# a .NET bundle even in a repo that also declares a .NET service; `all_foundations`
|
|
140
|
+
# keeps the unfiltered set for the gate message below. A direct "Foundation App"
|
|
141
|
+
# argument is parsed here too as an optional fast path.
|
|
63
142
|
- id: list_foundations
|
|
64
143
|
action: execute_script
|
|
65
144
|
script: { ref: actions }
|
|
66
145
|
subcommand: foundation-list
|
|
67
|
-
args: ["--args", "{{USER_INPUT}}"]
|
|
146
|
+
args: ["--args", "{{USER_INPUT}}", "--ecosystem", "{{lane_ecosystem}}"]
|
|
68
147
|
outputs:
|
|
69
148
|
- { name: foundations, from: "$.foundations" }
|
|
70
149
|
- { name: foundation_count, from: "$.count" }
|
|
@@ -72,23 +151,41 @@ workflow:
|
|
|
72
151
|
- { name: lane_ecosystems, from: "$.lane_ecosystems" }
|
|
73
152
|
- { name: preselect_name, from: "$.preselect_name" }
|
|
74
153
|
- { name: preselect_app, from: "$.preselect_app" }
|
|
75
|
-
- { name:
|
|
154
|
+
- { name: rejected_out, from: "$.rejected_out" }
|
|
155
|
+
|
|
156
|
+
# 1b. A third fast-path token used to be an output directory. Refused by name rather
|
|
157
|
+
# than ignored: silently dropping a path argument is how someone believes they
|
|
158
|
+
# scaffolded a sandbox and finds they filled their service lane.
|
|
159
|
+
- id: out_dir_given
|
|
160
|
+
action: set
|
|
161
|
+
condition: "{{rejected_out}} != "
|
|
162
|
+
vars: { out_ok: "" }
|
|
163
|
+
|
|
164
|
+
- id: no_out_dir
|
|
165
|
+
action: verify_context
|
|
166
|
+
require_true: out_ok
|
|
167
|
+
on_false: stop
|
|
168
|
+
on_false_message: >
|
|
169
|
+
`{{rejected_out}}` was given as an output directory, and foundation has none to give.
|
|
170
|
+
A scaffold is unpacked into the source path of the lane it is for — here lane
|
|
171
|
+
`{{lane_id}}` at `{{lane_root}}` — so that everything it writes is inside the subtree
|
|
172
|
+
that lane owns. Re-run as `/appweaver foundation {{preselect_name}} {{preselect_app}}`.
|
|
173
|
+
To scaffold somewhere else, declare a lane there with /appweaver select-stack and
|
|
174
|
+
pick that lane.
|
|
76
175
|
|
|
77
176
|
- id: have_foundations
|
|
78
177
|
action: verify_context
|
|
79
178
|
require_non_empty: foundations
|
|
80
179
|
on_empty: stop
|
|
81
180
|
on_empty_message: >
|
|
82
|
-
No foundation bundle
|
|
83
|
-
{{all_foundations}}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
/appweaver select-stack — implement generates INTO a project tree and does not
|
|
91
|
-
require that the kit be the thing that created it.
|
|
181
|
+
No foundation bundle ships for the `{{lane_id}}` lane's ecosystem ({{lane_ecosystem}}).
|
|
182
|
+
Bundles present: {{all_foundations}}. A foundation is a complete project in one
|
|
183
|
+
ecosystem's languages and build system, so one from another ecosystem is not a
|
|
184
|
+
scaffold for this lane — it is a second, foreign project inside it. Either add a
|
|
185
|
+
bundle under foundations/{{lane_ecosystem}}/<Name>/<Name>.pack.txt (see the pack
|
|
186
|
+
scripts in that directory), or scaffold `{{lane_root}}` with the stack's own tooling
|
|
187
|
+
(Spring Initializr, `npm create`, `cargo new`, …) — implement generates INTO a
|
|
188
|
+
project tree and does not require that the kit be the thing that created it.
|
|
92
189
|
|
|
93
190
|
# 1b. PICK the foundation. A matching fast-path arg auto-selects; a lone
|
|
94
191
|
# foundation auto-confirms; otherwise the user chooses from the list.
|
|
@@ -128,14 +225,13 @@ workflow:
|
|
|
128
225
|
action: execute_script
|
|
129
226
|
script: { ref: actions }
|
|
130
227
|
subcommand: foundation-locate
|
|
131
|
-
args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{
|
|
228
|
+
args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}"]
|
|
132
229
|
outputs:
|
|
133
230
|
- { name: locate_ok, from: "$.ok" }
|
|
134
231
|
- { name: locate_msg, from: "$.reason" }
|
|
135
232
|
- { name: available, from: "$.available" }
|
|
136
233
|
- { name: fname, from: "$.name" }
|
|
137
234
|
- { name: app, from: "$.app" }
|
|
138
|
-
- { name: out_dir, from: "$.out_dir" }
|
|
139
235
|
- { name: sentinel, from: "$.sentinel" }
|
|
140
236
|
- { name: file_count, from: "$.file_count" }
|
|
141
237
|
|
|
@@ -151,7 +247,9 @@ workflow:
|
|
|
151
247
|
prompt: |
|
|
152
248
|
Foundation: {{fname}} (sentinel {{sentinel}}, {{file_count}} files)
|
|
153
249
|
App Name: {{app}}
|
|
154
|
-
|
|
250
|
+
Lane: {{lane_id}} — {{lane_language}} ({{lane_ecosystem}})
|
|
251
|
+
Source: {{lane_root}} <- the whole bundle unpacks here
|
|
252
|
+
Tests: {{lane_test_path}} <- a test tree the bundle carries is lifted here
|
|
155
253
|
Rename {{sentinel}} -> {{app}} (dotted/underscored/hyphen/lower forms).
|
|
156
254
|
Unchanged: 'Projects' sample domain + AppWeaver.* framework packages. Proceed?
|
|
157
255
|
options: ["yes", "no"]
|
|
@@ -169,7 +267,7 @@ workflow:
|
|
|
169
267
|
action: execute_script
|
|
170
268
|
script: { ref: actions }
|
|
171
269
|
subcommand: foundation-unpack
|
|
172
|
-
args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{
|
|
270
|
+
args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}", "--dry-run"]
|
|
173
271
|
outputs:
|
|
174
272
|
- { name: has_collisions, from: "$.has_collisions" }
|
|
175
273
|
|
|
@@ -193,9 +291,11 @@ workflow:
|
|
|
193
291
|
action: execute_script
|
|
194
292
|
script: { ref: actions }
|
|
195
293
|
subcommand: foundation-unpack
|
|
196
|
-
args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{
|
|
294
|
+
args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}", "--test-path", "{{lane_test_path}}", "--force", "{{overwrite}}"]
|
|
197
295
|
outputs:
|
|
198
|
-
- { name: written,
|
|
296
|
+
- { name: written, from: "$.written" }
|
|
297
|
+
- { name: tests_moved, from: "$.tests_moved" }
|
|
298
|
+
- { name: tests_message, from: "$.tests_message" }
|
|
199
299
|
|
|
200
300
|
# 5. Optional reference sample.
|
|
201
301
|
- id: ask_reference
|
|
@@ -209,7 +309,7 @@ workflow:
|
|
|
209
309
|
condition: "{{want_reference}} == yes"
|
|
210
310
|
script: { ref: actions }
|
|
211
311
|
subcommand: foundation-stash-dbcontext
|
|
212
|
-
args: ["--out", "{{
|
|
312
|
+
args: ["--out", "{{lane_root}}"]
|
|
213
313
|
outputs:
|
|
214
314
|
- { name: stash_found, from: "$.found" }
|
|
215
315
|
- { name: db_context, from: "$.dbcontext" }
|
|
@@ -220,7 +320,7 @@ workflow:
|
|
|
220
320
|
condition: "{{want_reference}} == yes"
|
|
221
321
|
script: { ref: actions }
|
|
222
322
|
subcommand: foundation-unpack
|
|
223
|
-
args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{
|
|
323
|
+
args: ["--name", "{{fname}}", "--app", "{{app}}", "--out", "{{lane_root}}", "--test-path", "{{lane_test_path}}", "--reference"]
|
|
224
324
|
outputs:
|
|
225
325
|
- { name: ref_written, from: "$.written" }
|
|
226
326
|
|
|
@@ -248,7 +348,7 @@ workflow:
|
|
|
248
348
|
action: execute_script
|
|
249
349
|
script: { ref: actions }
|
|
250
350
|
subcommand: foundation-verify
|
|
251
|
-
args: ["--out", "{{
|
|
351
|
+
args: ["--out", "{{lane_root}}", "--sentinel", "{{sentinel}}"]
|
|
252
352
|
outputs:
|
|
253
353
|
- { name: clean, from: "$.clean" }
|
|
254
354
|
- { name: residue, from: "$.residue" }
|
|
@@ -256,10 +356,14 @@ workflow:
|
|
|
256
356
|
outputs:
|
|
257
357
|
foundation: "{{fname}}"
|
|
258
358
|
app: "{{app}}"
|
|
259
|
-
|
|
359
|
+
lane: "{{lane_id}}"
|
|
360
|
+
source_path: "{{lane_root}}"
|
|
361
|
+
test_path: "{{lane_test_path}}"
|
|
260
362
|
files_written: "{{written}}"
|
|
363
|
+
tests_moved: "{{tests_moved}}"
|
|
364
|
+
tests_note: "{{tests_message}}"
|
|
261
365
|
reference_files: "{{ref_written}}"
|
|
262
366
|
sentinel_clean: "{{clean}}"
|
|
263
367
|
sentinel_residue: "{{residue}}"
|
|
264
|
-
note: "Foundation scaffolded into {{
|
|
265
|
-
next: "/appweaver select-feature
|
|
368
|
+
note: "Foundation scaffolded into the {{lane_id}} lane at {{lane_root}} ({{written}} file(s), sentinel renamed to {{app}}); {{tests_message}}. Everything written is inside the subtree that lane owns, which is where implement generates and build/test/run act. Configure your AppWeaver.* NuGet feed and run the AppHost to confirm the scaffold starts before building on it."
|
|
369
|
+
next: "Another lane still to scaffold? Run /appweaver foundation again and pick it — one bundle fills one lane. Otherwise /appweaver select-feature (pick the feature to build; every stage from specify onward reads that active feature and stops without it. If this workspace has governed projects under guardrails/projects/, run /appweaver select-domain first — it is one-time and optional.)"
|