aw-spec-cli 0.1.21 → 0.1.23

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.
Files changed (51) hide show
  1. package/assets/.appweaver/STACK-PROFILES.md +87 -8
  2. package/assets/.appweaver/orchestrator/modules/delta-plan.orch.yaml +30 -0
  3. package/assets/.appweaver/orchestrator/orchestrator.py +47 -3
  4. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-plan.orch.yaml +41 -8
  5. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +62 -2
  6. package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +18 -5
  7. package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +1 -1
  8. package/assets/.appweaver/orchestrator/workflows/aw-spec-dependency-tree.orch.yaml +4 -1
  9. package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +115 -27
  10. package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +5 -1
  11. package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +225 -27
  12. package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +4 -15
  13. package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +1 -1
  14. package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +39 -0
  15. package/assets/.appweaver/orchestrator/workflows/aw-spec-recall-delta.orch.yaml +28 -7
  16. package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +3 -1
  17. package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +104 -1
  18. package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +159 -0
  19. package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +386 -0
  20. package/assets/.appweaver/orchestrator/workflows/aw-spec-workflow-model.orch.yaml +153 -0
  21. package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +203 -212
  22. package/assets/.appweaver/scripts/bash/aw-xref.sh +4 -1
  23. package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +191 -224
  24. package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +4 -1
  25. package/assets/.appweaver/scripts/python/aw-crstate.py +127 -2
  26. package/assets/.appweaver/scripts/python/aw-gaps.py +119 -0
  27. package/assets/.appweaver/scripts/python/aw-payload-parity.py +17 -1
  28. package/assets/.appweaver/scripts/python/aw-plan-coverage.py +84 -4
  29. package/assets/.appweaver/scripts/python/aw-plangate.py +216 -0
  30. package/assets/.appweaver/scripts/python/aw-principles.py +108 -0
  31. package/assets/.appweaver/scripts/python/aw-stack.py +6 -0
  32. package/assets/.appweaver/scripts/python/aw-xref.py +141 -4
  33. package/assets/.appweaver/stack-profiles/angular.json +26 -0
  34. package/assets/.appweaver/stack-profiles/blazor-wasm.json +38 -0
  35. package/assets/.appweaver/stack-profiles/dotnet-webapi.json +38 -0
  36. package/assets/.appweaver/stack-profiles/java-spring.json +19 -0
  37. package/assets/.appweaver/stack-profiles/nextjs.json +26 -0
  38. package/assets/.appweaver/stack-profiles/node-express.json +26 -0
  39. package/assets/.appweaver/stack-profiles/python-fastapi.json +15 -0
  40. package/assets/.appweaver/stack-profiles/react-vite.json +26 -0
  41. package/assets/.appweaver/stack-profiles/sveltekit.json +26 -0
  42. package/assets/.appweaver/stack-profiles/vue-vite.json +26 -0
  43. package/assets/.appweaver/templates/ui-model-template.md +67 -0
  44. package/assets/.appweaver/templates/workflow-model-template.md +62 -0
  45. package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +3 -3
  46. package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +5 -123
  47. package/assets/foundations/java/Hexagonal/Hexagonal.pack.txt +784 -0
  48. package/assets/foundations/java/Hexagonal/pack-foundation.sh +357 -0
  49. package/assets/foundations/java/Hexagonal/unpack-foundation.sh +399 -0
  50. package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +44 -0
  51. package/package.json +1 -1
@@ -0,0 +1,399 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # unpack-foundation.sh — Java foundation unpacker for AW-Spec
4
+ #
5
+ # Unpacks an AW-Spec Java foundation pack into a target application,
6
+ # replacing the Java package sentinel with the target application package.
7
+ #
8
+ # Java foundation contract:
9
+ # Sentinel: io.appweaver
10
+ #
11
+ # Example:
12
+ # ./unpack-foundation.sh \
13
+ # --pack-file ./Hexagonal.pack.txt \
14
+ # --target-app-name FlightBooking \
15
+ # --out-dir backend
16
+ #
17
+
18
+ set -euo pipefail
19
+
20
+ # ----------------------------------------------------------- defaults --------------
21
+
22
+ PACK_FILE=''
23
+ TARGET_APP_NAME=''
24
+ OUT_DIR='src'
25
+ SENTINEL=''
26
+ FORCE=0
27
+ DRYRUN=0
28
+
29
+ # ----------------------------------------------------------- usage ----------------
30
+
31
+ usage() {
32
+ cat <<'EOF'
33
+ Usage:
34
+ unpack-foundation.sh \
35
+ --pack-file <pack-file> \
36
+ --target-app-name <app-name> \
37
+ --out-dir <output-dir> \
38
+ [--sentinel <sentinel>] \
39
+ [--force] \
40
+ [--dry-run]
41
+
42
+ Options:
43
+ --pack-file Foundation pack file.
44
+ --target-app-name Target application name.
45
+ --out-dir Output directory.
46
+ --sentinel Optional sentinel override.
47
+ --force Allow existing destination files to be overwritten.
48
+ --dry-run Show what would be written without changing files.
49
+ -h, --help Show this help.
50
+ EOF
51
+ }
52
+
53
+ # ----------------------------------------------------------- error -----------------
54
+
55
+ error() {
56
+ echo "Error: $*" >&2
57
+ exit 1
58
+ }
59
+
60
+ # ----------------------------------------------------------- arguments ------------
61
+
62
+ while [ "$#" -gt 0 ]; do
63
+ case "$1" in
64
+ --pack-file)
65
+ [ "$#" -ge 2 ] || error "--pack-file requires a value"
66
+ PACK_FILE="$2"
67
+ shift 2
68
+ ;;
69
+
70
+ --target-app-name)
71
+ [ "$#" -ge 2 ] || error "--target-app-name requires a value"
72
+ TARGET_APP_NAME="$2"
73
+ shift 2
74
+ ;;
75
+
76
+ --out-dir)
77
+ [ "$#" -ge 2 ] || error "--out-dir requires a value"
78
+ OUT_DIR="$2"
79
+ shift 2
80
+ ;;
81
+
82
+ --sentinel)
83
+ [ "$#" -ge 2 ] || error "--sentinel requires a value"
84
+ SENTINEL="$2"
85
+ shift 2
86
+ ;;
87
+
88
+ --force)
89
+ FORCE=1
90
+ shift
91
+ ;;
92
+
93
+ --dry-run)
94
+ DRYRUN=1
95
+ shift
96
+ ;;
97
+
98
+ -h|--help)
99
+ usage
100
+ exit 0
101
+ ;;
102
+
103
+ *)
104
+ error "Unknown argument: $1"
105
+ ;;
106
+ esac
107
+ done
108
+
109
+ # ----------------------------------------------------------- validate args ----------
110
+
111
+ [ -n "$PACK_FILE" ] || error "--pack-file is required"
112
+ [ -n "$TARGET_APP_NAME" ] || error "--target-app-name is required"
113
+
114
+ [ -f "$PACK_FILE" ] || error "Pack file not found: $PACK_FILE"
115
+
116
+ case "$TARGET_APP_NAME" in
117
+ *[!A-Za-z0-9._-]*)
118
+ error "Invalid application name '$TARGET_APP_NAME'. Allowed characters: A-Z a-z 0-9 . _ -"
119
+ ;;
120
+ esac
121
+
122
+ # ----------------------------------------------------------- read pack metadata ----
123
+
124
+ PACK_SENTINEL=''
125
+
126
+ while IFS= read -r line || [ -n "$line" ]; do
127
+ line="${line%$'\r'}"
128
+
129
+ case "$line" in
130
+ '# sentinel: '*)
131
+ PACK_SENTINEL="${line#'# sentinel: '}"
132
+ break
133
+ ;;
134
+ esac
135
+ done < "$PACK_FILE"
136
+
137
+ if [ -n "$SENTINEL" ]; then
138
+ :
139
+ elif [ -n "$PACK_SENTINEL" ]; then
140
+ SENTINEL="$PACK_SENTINEL"
141
+ else
142
+ error "Could not determine sentinel from pack. Use --sentinel."
143
+ fi
144
+
145
+ [ -n "$SENTINEL" ] || error "Sentinel cannot be empty"
146
+
147
+ # ----------------------------------------------------------- Java replacement -------
148
+
149
+ #
150
+ # Java package rules:
151
+ #
152
+ # io.appweaver
153
+ # ->
154
+ # <lowercase application name>
155
+ #
156
+ # io/appweaver
157
+ # ->
158
+ # <lowercase application name>
159
+ #
160
+ # The application directory itself keeps the original user-provided
161
+ # application name/casing.
162
+ #
163
+
164
+ TARGET_PACKAGE="$(printf '%s' "$TARGET_APP_NAME" | tr '[:upper:]' '[:lower:]')"
165
+
166
+ S_DOT="$SENTINEL"
167
+ T_DOT="$TARGET_PACKAGE"
168
+
169
+ S_PATH="$(printf '%s' "$SENTINEL" | tr '.' '/')"
170
+ T_PATH="$TARGET_PACKAGE"
171
+
172
+ FROM=()
173
+ TO=()
174
+
175
+ add_form() {
176
+ FROM+=("$1")
177
+ TO+=("$2")
178
+ }
179
+
180
+ add_form "$S_DOT" "$T_DOT"
181
+ add_form "$S_PATH" "$T_PATH"
182
+
183
+ # ----------------------------------------------------------- replacement ------------
184
+
185
+ #
186
+ # Apply replacements from longest source string to shortest.
187
+ # This avoids partial replacement problems when one replacement
188
+ # string is a prefix of another.
189
+ #
190
+
191
+ apply_repl() {
192
+ local input="$1"
193
+ local i
194
+ local from
195
+ local to
196
+
197
+ REPL_OUT="$input"
198
+
199
+ for i in "${!FROM[@]}"; do
200
+ from="${FROM[$i]}"
201
+ to="${TO[$i]}"
202
+
203
+ REPL_OUT="${REPL_OUT//$from/$to}"
204
+ done
205
+ }
206
+
207
+ # ----------------------------------------------------------- output root ------------
208
+
209
+ if [ "$OUT_DIR" = "." ]; then
210
+ OUT_ROOT="$TARGET_APP_NAME"
211
+ else
212
+ OUT_ROOT="$OUT_DIR/$TARGET_APP_NAME"
213
+ fi
214
+
215
+ # ----------------------------------------------------------- parse pack blocks -------
216
+
217
+ rels=()
218
+ encs=()
219
+ contents=()
220
+ dir_rels=()
221
+
222
+ in_block=0
223
+ cur_rel=''
224
+ cur_enc=''
225
+ cur_content=''
226
+ first_line=1
227
+
228
+ file_re='^<<<FILE path="([^"]+)"( encoding="([^"]+)")?>>>[[:space:]]*$'
229
+ dir_re='^<<<DIR path="([^"]+)">>>[[:space:]]*$'
230
+
231
+ while IFS= read -r line || [ -n "$line" ]; do
232
+ line="${line%$'\r'}"
233
+
234
+ if [ "$in_block" -eq 0 ]; then
235
+
236
+ if [[ "$line" =~ $file_re ]]; then
237
+ cur_rel="${BASH_REMATCH[1]}"
238
+ cur_enc="${BASH_REMATCH[3]:-}"
239
+ in_block=1
240
+ first_line=1
241
+ cur_content=''
242
+
243
+ elif [[ "$line" =~ $dir_re ]]; then
244
+ dir_rels+=("${BASH_REMATCH[1]}")
245
+ fi
246
+
247
+ else
248
+
249
+ if [ "$line" = '<<<ENDFILE>>>' ]; then
250
+ rels+=("$cur_rel")
251
+ encs+=("$cur_enc")
252
+ contents+=("$cur_content")
253
+
254
+ in_block=0
255
+
256
+ elif [ "$first_line" -eq 1 ]; then
257
+ cur_content="$line"
258
+ first_line=0
259
+
260
+ else
261
+ cur_content="$cur_content"$'\n'"$line"
262
+ fi
263
+
264
+ fi
265
+
266
+ done < "$PACK_FILE"
267
+
268
+ [ "${#rels[@]}" -gt 0 ] || {
269
+ echo "Error: No <<<FILE>>> blocks found in pack: $PACK_FILE" >&2
270
+ exit 1
271
+ }
272
+
273
+ # ----------------------------------------------------------- resolve directories ----
274
+
275
+ is_empty_java_package_parent() {
276
+ local rel="$1"
277
+
278
+ case "$rel" in
279
+ src/main/java/io|src/test/java/io)
280
+ return 0
281
+ ;;
282
+ *)
283
+ return 1
284
+ ;;
285
+ esac
286
+ }
287
+
288
+ dir_dests=()
289
+
290
+ for rel in "${dir_rels[@]}"; do
291
+ if is_empty_java_package_parent "$rel"; then
292
+ continue
293
+ fi
294
+
295
+ apply_repl "$rel"
296
+ dir_dests+=("$OUT_ROOT/$REPL_OUT")
297
+ done
298
+
299
+ # ----------------------------------------------------------- resolve files ----------
300
+
301
+ dests=()
302
+
303
+ for rel in "${rels[@]}"; do
304
+ apply_repl "$rel"
305
+ dests+=("$OUT_ROOT/$REPL_OUT")
306
+ done
307
+
308
+ # ----------------------------------------------------------- conflict guard ----------
309
+
310
+ if [ "$FORCE" -eq 0 ] && [ "$DRYRUN" -eq 0 ]; then
311
+
312
+ for dest in "${dests[@]}"; do
313
+ if [ -e "$dest" ]; then
314
+ error "Destination already exists: $dest (use --force to overwrite)"
315
+ fi
316
+ done
317
+
318
+ fi
319
+
320
+ # ----------------------------------------------------------- base64 decoder ----------
321
+
322
+ decode_base64() {
323
+ if base64 --help 2>&1 | grep -q -- '--decode'; then
324
+ base64 --decode
325
+ else
326
+ base64 -D
327
+ fi
328
+ }
329
+
330
+ # ----------------------------------------------------------- create directories -------
331
+
332
+ directories_created=0
333
+
334
+ if [ "$DRYRUN" -eq 1 ]; then
335
+
336
+ for dest in "${dir_dests[@]}"; do
337
+ echo "WOULD CREATE DIRECTORY $dest"
338
+ done
339
+
340
+ else
341
+
342
+ for dest in "${dir_dests[@]}"; do
343
+ mkdir -p "$dest"
344
+ directories_created=$((directories_created + 1))
345
+ done
346
+
347
+ fi
348
+
349
+ # ----------------------------------------------------------- write files -------------
350
+
351
+ written=0
352
+
353
+ if [ "$DRYRUN" -eq 1 ]; then
354
+
355
+ for i in "${!rels[@]}"; do
356
+ echo "WOULD WRITE ${dests[$i]}"
357
+ done
358
+
359
+ else
360
+
361
+ for i in "${!rels[@]}"; do
362
+
363
+ dest="${dests[$i]}"
364
+ enc="${encs[$i]}"
365
+ content="${contents[$i]}"
366
+
367
+ mkdir -p "$(dirname "$dest")"
368
+
369
+ if [ "$enc" = "base64" ]; then
370
+ printf '%s' "$content" | decode_base64 > "$dest"
371
+ else
372
+ apply_repl "$content"
373
+ printf '%s\n' "$REPL_OUT" > "$dest"
374
+ fi
375
+
376
+ written=$((written + 1))
377
+
378
+ done
379
+
380
+ fi
381
+
382
+ # ----------------------------------------------------------- summary -----------------
383
+
384
+ echo
385
+
386
+ if [ "$DRYRUN" -eq 1 ]; then
387
+
388
+ echo "DryRun: ${#rels[@]} file(s) and ${#dir_rels[@]} directorie(s) would be written to:"
389
+ echo " $OUT_ROOT"
390
+
391
+ else
392
+
393
+ echo "Unpacked $written file(s) and $directories_created directorie(s) to:"
394
+ echo " $OUT_ROOT"
395
+
396
+ fi
397
+
398
+ echo " Sentinel: $SENTINEL"
399
+ echo " Package: $TARGET_PACKAGE"
@@ -50,6 +50,50 @@ The auditing interfaces are the ones most often reported missing: they are in a
50
50
  package from the aggregate types, so a project that references `AppWeaver.DomainAbstraction`
51
51
  alone cannot see `IAggregateChild` at all. A rule that requires one requires its package.
52
52
 
53
+ ## The other direction — framework types that shadow DOMAIN names
54
+
55
+ The map above is about a framework type the code cannot find. The opposite failure is a
56
+ framework type it finds *too easily*: a domain noun that the ambient scope already defines.
57
+
58
+ `Microsoft.NET.Sdk.Web` turns on implicit usings, so `System`, `System.Collections.Generic`,
59
+ `System.IO`, `System.Threading.Tasks`, `Microsoft.AspNetCore.Http` and
60
+ `Microsoft.AspNetCore.Routing` are in scope in **every** file without appearing in any
61
+ `using` line. A domain type sharing a name with one of theirs — `Route`, `Task`, `Activity`,
62
+ `Path`, `File`, `Timer`, `Index`, `Range`, `Version`, `Results`, `Endpoint`, `Environment`,
63
+ `Stream`, `Type`, `Queue`, `Stack` — is then an **ambiguous reference, CS0104**:
64
+
65
+ ```csharp
66
+ using Acme.Clinic.Service.Care.Domain; // brings in the domain Route
67
+
68
+ public static Route? Route(string? value) // CS0104: Care.Domain.Route
69
+ // vs Microsoft.AspNetCore.Routing.Route
70
+ ```
71
+
72
+ The conflict is invisible in the file. Nothing in it names `Microsoft.AspNetCore.Routing`,
73
+ so the bare name reads as correct and fails only at build.
74
+
75
+ **The domain name is not the problem and never changes.** It is the ubiquitous language,
76
+ the plan spells it, and the code spells it back verbatim. What changes is how it is
77
+ written at the use site:
78
+
79
+ ```csharp
80
+ public static Domain.Route? Route(string? value) =>
81
+ string.IsNullOrWhiteSpace(value) ? null : Domain.Route.From(value);
82
+ ```
83
+
84
+ - **Qualify with the type's own enclosing namespace segment** — `Domain.Route` — at every
85
+ site the type is named: declarations, parameters, return types, generic arguments and
86
+ `<see cref="…"/>` comments.
87
+ - **Not a `using` alias and not the full namespace path.** One convention, so two features
88
+ never disambiguate the same name two ways.
89
+ - **Never rename the domain type** to avoid the clash, and never drop the domain type in
90
+ favour of the framework one.
91
+
92
+ `/aw-spec-implement` is told which of *this* plan's element names are affected before it
93
+ writes anything — the list comes from the lane's stack profile (`reserved_names` in
94
+ `.appweaver/stack-profiles/<id>.json`), so it is the same answer on every run rather than
95
+ something each pass has to notice for itself.
96
+
53
97
  ## Rules
54
98
 
55
99
  - **Cite a framework type with its namespace** in plans and generated code, and add the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aw-spec-cli",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "AppWeaver Spec-Driven Development CLI - scaffolds projects with vertical slices, DDD, CQRS, and agent skills",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",