aw-spec-cli 0.1.26 → 0.1.27
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/.agents/skills/appweaver/SKILL.md +7 -47
- package/assets/.appweaver/ORCH-DIALECT.md +13 -9
- package/assets/.appweaver/RULE-PARTITION.md +3 -3
- package/assets/.appweaver/STACK-PROFILES.md +188 -101
- package/assets/.appweaver/domain.json +1 -1
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +38 -10
- package/assets/.appweaver/orchestrator/LAUNCHER.md +88 -0
- package/assets/.appweaver/orchestrator/README.md +120 -53
- package/assets/.appweaver/orchestrator/adapters/claude_agent_sdk.py +1 -1
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +6 -4
- package/assets/.appweaver/orchestrator/modules/foundation-lane.orch.yaml +237 -0
- package/assets/.appweaver/orchestrator/modules/foundation-pack.orch.yaml +349 -0
- package/assets/.appweaver/orchestrator/modules/foundation-spec.orch.yaml +381 -0
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/orchestrator.py +162 -63
- package/assets/.appweaver/orchestrator/rev-templates/domain-constitution.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +5 -5
- package/assets/.appweaver/orchestrator/rev-templates/frontend-rule-file.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +14 -14
- package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +2 -2
- package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +1 -1
- package/assets/.appweaver/orchestrator/rev-templates/ui-constitution.skeleton.md +3 -3
- package/assets/.appweaver/orchestrator/workflows/_smoke.orch.yaml +41 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-add-component.orch.yaml +1 -1
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +13 -13
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice-delta.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +61 -12
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +172 -293
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +29 -28
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +53 -53
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-plan.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +4 -4
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-bounded-context.orch.yaml +26 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +21 -15
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +14 -14
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +2 -2
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-model.orch.yaml +3 -3
- package/assets/.appweaver/orchestrator/workflows/aw-spec-ui-prototype.orch.yaml +6 -6
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +284 -128
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +224 -70
- package/assets/.appweaver/scripts/python/aw-foundation.py +809 -20
- package/assets/.appweaver/scripts/python/aw-inventory.py +28 -21
- package/assets/.appweaver/scripts/python/aw-plan-coverage.py +2 -2
- package/assets/.appweaver/scripts/python/aw-rev-constitution.py +6 -6
- package/assets/.appweaver/scripts/python/aw-stack.py +195 -89
- package/assets/.appweaver/stack-profiles/angular.json +2 -1
- package/assets/.appweaver/stack-profiles/blazor-wasm.json +2 -1
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +16 -3
- package/assets/.appweaver/stack-profiles/go-chi.json +6 -1
- package/assets/.appweaver/stack-profiles/java-spring.json +12 -5
- package/assets/.appweaver/stack-profiles/nextjs.json +2 -1
- package/assets/.appweaver/stack-profiles/node-express.json +8 -2
- package/assets/.appweaver/stack-profiles/python-fastapi.json +9 -2
- package/assets/.appweaver/stack-profiles/react-vite.json +2 -1
- package/assets/.appweaver/stack-profiles/sveltekit.json +2 -1
- package/assets/.appweaver/stack-profiles/vue-vite.json +2 -1
- package/assets/.appweaver/stacks.json +37 -1
- package/assets/.appweaver/templates/constitution-ui-template.md +2 -2
- package/assets/.claude/skills/appweaver/SKILL.md +7 -46
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.Reference.rules.json +31 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.Reference.rules.json +16 -0
- package/assets/foundations/foundation.spec.template.txt +73 -0
- package/assets/foundations/java/Hexagonal/Hexagonal.pack.txt +1 -1
- package/assets/foundations/java/Hexagonal/unpack-foundation.sh +4 -5
- package/assets/foundations/node/NextAppRouter/NextAppRouter.spec.txt +68 -0
- package/assets/guardrails/README.md +31 -17
- package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain-rule-bindings.md +5 -5
- package/assets/guardrails/{projects/dotnet-default-project → project-specific}/tech-rule-bindings.md +151 -114
- package/assets/guardrails/technology/dotnet-webapi/PACKAGING.md +58 -4
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/dependency-policy.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/framework-api-map.md +1 -1
- package/assets/guardrails/technology/dotnet-webapi/testing.md +1 -1
- package/assets/guardrails/technology/nextjs/api-client.md +1 -1
- package/assets/guardrails/technology/nextjs/component-architecture.md +1 -1
- package/assets/guardrails/technology/nextjs/component-conventions.md +2 -2
- package/assets/guardrails/technology/nextjs/design-system.md +1 -1
- package/assets/guardrails/technology/nextjs/frontend-testing.md +1 -1
- package/assets/guardrails/technology/nextjs/routing-navigation.md +1 -1
- package/assets/guardrails/technology/nextjs/ui-constitution.md +3 -3
- package/package.json +1 -1
- package/assets/.agents/skills/appweaver-demo/SKILL.md +0 -59
- package/assets/.appweaver/orchestrator/.smoke-out/smoke.md +0 -6
- package/assets/.appweaver/orchestrator/workflows/aw-spec-demo.orch.yaml +0 -100
- package/assets/.claude/skills/appweaver-demo/SKILL.md +0 -59
- package/assets/.kilocode/agent.json +0 -28
- package/assets/.kilocode/workflows/appweaver-demo.md +0 -59
- package/assets/.kilocode/workflows/appweaver.md +0 -55
- /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/domain/domain-requirements.md +0 -0
- /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/frontend/ui-requirements.md +0 -0
- /package/assets/guardrails/{projects/dotnet-default-project → project-specific}/service/technical-requirements.md +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
#
|
|
3
3
|
# aw-spec-actions.sh — common action dispatcher for the aw-spec orchestrator.
|
|
4
4
|
#
|
|
5
|
-
# ONE shared script for every integration (.claude / .agents / .
|
|
5
|
+
# ONE shared script for every integration (.claude / .agents / .kilo). The
|
|
6
6
|
# orchestrator workflows reach these subcommands through `execute_script` steps.
|
|
7
7
|
# Each subcommand performs deterministic, governance-safe work and prints a single
|
|
8
8
|
# JSON object on stdout. NONE of these actions author spec/code content — content
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
# (the FILE signals for "does this lane exist"; resolve-tech carries the rule signal)
|
|
50
50
|
# list-features [--preselect <name>] [--bundle <dir>] -> {features[], names[], count, select_options[], sentinel} (specs/ ∪ features the OKF concepts serve)
|
|
51
51
|
# set-feature --feature <name> -> {feature, feature_dir, spec_file, existed, created, spec_exists}
|
|
52
|
+
# default-scope --context <name> --feature <name> -> {created, skipped_reason, context_dir, feature_dir}
|
|
53
|
+
# (creates and activates both only on a project with no specs yet)
|
|
52
54
|
# version-read [--feature-dir <path>] -> {feature, version, active_change, exists, bootstrap_pending}
|
|
53
55
|
# version-init [--feature-dir <path>] [--bootstrap] -> {…, created, bootstrap_pending} (create active_version.json v1.0; --bootstrap marks a FIRST draft in progress)
|
|
54
56
|
# version-bootstrap-done [--feature-dir <path>] -> {…, cleared} (the ONLY clearer of the --bootstrap marker; call it when the first draft is finished)
|
|
@@ -63,11 +65,26 @@
|
|
|
63
65
|
# -> {ok, total, names[], names_csv, elements[], gaps[], gap_count, gaps_text}
|
|
64
66
|
# (which plan elements the lane's code names and the model reported done)
|
|
65
67
|
# detect-project -> {scaffolded, markers[]}
|
|
66
|
-
# foundation-list [--
|
|
68
|
+
# foundation-list [--kind <k>] [--profile <id>] [--ecosystem <e>] [--lane-id <id>] [--args '<F> <A>']
|
|
69
|
+
# -> {options[], entries[], count, foundations[], specs[], preselect_option, preselect_app, rejected_out}
|
|
70
|
+
# (one lane's pick list: its packs + specs, then [manual spec] and [skip])
|
|
71
|
+
# foundation-choice --selection <option> [same filters as foundation-list]
|
|
72
|
+
# -> {ok, route (pack|spec|manual|skip), name, file, reason}
|
|
73
|
+
# foundation-spec-capture --lane <id> [--kind <k>] (--file <path> | --input <path|text>)
|
|
74
|
+
# -> {ok, spec_path, source, chars, title, reason}
|
|
75
|
+
# foundation-lane-files --source <p> [--test <p>] [--workdir <p>] [--profile <id>]
|
|
76
|
+
# -> {has_files, count, sample[], generated[], root_files[], folders[], workdir, summary}
|
|
77
|
+
# foundation-snapshot --out <repo-relative file> -> {ok, snapshot, entries}
|
|
78
|
+
# foundation-outside --snapshot <file> --lane <lane root folder> [--undo]
|
|
79
|
+
# -> {clean, created[], changed[], deleted[], removed[], summary}
|
|
67
80
|
# foundation-locate --name <F> --app <A> [--out <d>]
|
|
68
|
-
# -> {ok, reason, name, app, out_dir, pack_file, unpacker, runner, sentinel, file_count, available[]}
|
|
69
|
-
# foundation-unpack --name <F> --app <A> --out <d> [--dry-run] [--force <yes|no>] [--reference]
|
|
70
|
-
# -> {ok, mode, written, has_collisions, stdout}
|
|
81
|
+
# -> {ok, reason, name, app, out_dir, pack_file, unpacker, runner, sentinel, file_count, has_reference, available[]}
|
|
82
|
+
# foundation-unpack --name <F> --app <A> --out <d> [--test-path <d>] [--dry-run] [--force <yes|no>] [--reference]
|
|
83
|
+
# -> {ok, mode, written, has_collisions, tests_moved, tests_message, stdout}
|
|
84
|
+
# foundation-rules --name <F> --app <A> --out <lane root folder> [--reference] [--state <folder>]
|
|
85
|
+
# -> {ok, reason, rules_file, rules, merges[], merge_count, stale[], note}
|
|
86
|
+
# (what the pack's <pack name>.rules.json asks of this unpack; puts the files to merge aside)
|
|
87
|
+
# foundation-rules-done [--state <folder>] -> {ok, removed}
|
|
71
88
|
# foundation-verify --out <d> --sentinel <s> -> {clean, residue[]}
|
|
72
89
|
# list-components -> {components[]} (foundations/dotnet/components/* with integration.md)
|
|
73
90
|
# find-host-project -> {found, program_cs, project_dir, csproj}
|
|
@@ -86,7 +103,9 @@
|
|
|
86
103
|
# (design-extract / design-reconcile / design-paths are RETIRED — the path-tagged
|
|
87
104
|
# four-document model is gone. "Which files is this slice made of" is now an OKF
|
|
88
105
|
# query: aw-xref concept-paths / concept-upsert --merge.)
|
|
89
|
-
# build-compile [--feature-dir <path>]
|
|
106
|
+
# build-compile [--feature-dir <path>] [--kind backend|frontend]
|
|
107
|
+
# -> {ok, tool, ran, cmd, log_tail}
|
|
108
|
+
# (--kind builds that one declared lane; ignored when no lane is declared)
|
|
90
109
|
# test-run [--feature-dir <path>] -> {ok, tool, ran, cmd, log_tail,
|
|
91
110
|
# failing_excerpt, failing_lanes[],
|
|
92
111
|
# no_setup, setup_reason, setup_missing[]}
|
|
@@ -163,7 +182,7 @@ cmd_resolve_domain() {
|
|
|
163
182
|
fi
|
|
164
183
|
fi
|
|
165
184
|
|
|
166
|
-
local rulemap="$repo_root/guardrails
|
|
185
|
+
local rulemap="$repo_root/guardrails/$domain/domain-rule-bindings.md"
|
|
167
186
|
local rulemap_exists=false
|
|
168
187
|
[ -n "$domain" ] && [ -f "$rulemap" ] && rulemap_exists=true
|
|
169
188
|
|
|
@@ -188,7 +207,7 @@ cmd_resolve_domain() {
|
|
|
188
207
|
|
|
189
208
|
# ──────────────────────────────── resolve-tech ─────────────────────────────
|
|
190
209
|
# Technical rule-file mapping — parallel to resolve-domain, but reads
|
|
191
|
-
# guardrails
|
|
210
|
+
# guardrails/<project>/tech-rule-bindings.md. The TECHNICAL rule files live in the
|
|
192
211
|
# shared per-stack corpora at guardrails/technology/<stack>/ (rules) and
|
|
193
212
|
# guardrails/technology/<stack>/examples/ (the worked sample for a rule file), and are
|
|
194
213
|
# configured per-command in this map, so they can be tuned per project WITHOUT editing the
|
|
@@ -239,7 +258,7 @@ repo = os.environ["AW_REPO"]
|
|
|
239
258
|
domain = os.environ.get("AW_DOMAIN", "")
|
|
240
259
|
command = os.environ.get("AW_COMMAND", "")
|
|
241
260
|
|
|
242
|
-
techmap = os.path.join(repo, "guardrails",
|
|
261
|
+
techmap = os.path.join(repo, "guardrails", domain, "tech-rule-bindings.md")
|
|
243
262
|
exists = bool(domain) and os.path.isfile(techmap)
|
|
244
263
|
|
|
245
264
|
# WHAT A PLACEHOLDER IS FOR. A technical rule corpus is stack-partitioned —
|
|
@@ -638,11 +657,12 @@ EOF
|
|
|
638
657
|
}
|
|
639
658
|
|
|
640
659
|
# ─────────────────────────────── list-domains ──────────────────────────────
|
|
641
|
-
# The
|
|
642
|
-
#
|
|
643
|
-
#
|
|
644
|
-
#
|
|
645
|
-
# map several steps before the technical one and a half-built
|
|
660
|
+
# The projects: immediate subdirs of guardrails/ that carry a rule map. The map is what
|
|
661
|
+
# makes a directory a project, exactly as a *.pack.txt is what makes a foundations/
|
|
662
|
+
# directory a foundation — the shared corpora technology/ and domain/ sit beside the
|
|
663
|
+
# projects and carry no map, so they are never listed. EITHER map qualifies, because
|
|
664
|
+
# onboard writes the domain map several steps before the technical one and a half-built
|
|
665
|
+
# project must stay visible.
|
|
646
666
|
cmd_list_domains() {
|
|
647
667
|
while [ $# -gt 0 ]; do
|
|
648
668
|
case "$1" in
|
|
@@ -651,7 +671,7 @@ cmd_list_domains() {
|
|
|
651
671
|
esac
|
|
652
672
|
done
|
|
653
673
|
local repo_root; repo_root=$(get_repo_root)
|
|
654
|
-
local base="$repo_root/guardrails
|
|
674
|
+
local base="$repo_root/guardrails"
|
|
655
675
|
local -a domains=()
|
|
656
676
|
if [ -d "$base" ]; then
|
|
657
677
|
local d
|
|
@@ -677,7 +697,7 @@ cmd_set_domain() {
|
|
|
677
697
|
[ -n "$domain" ] || { echo "set-domain: --domain is required" >&2; return 1; }
|
|
678
698
|
|
|
679
699
|
local repo_root; repo_root=$(get_repo_root)
|
|
680
|
-
local map_rel="guardrails
|
|
700
|
+
local map_rel="guardrails/$domain/domain-rule-bindings.md"
|
|
681
701
|
local map_exists=false
|
|
682
702
|
[ -f "$repo_root/$map_rel" ] && map_exists=true
|
|
683
703
|
|
|
@@ -728,7 +748,7 @@ except Exception:
|
|
|
728
748
|
' 2>/dev/null || echo ""
|
|
729
749
|
}
|
|
730
750
|
|
|
731
|
-
# The declared
|
|
751
|
+
# The declared LANE ROOT FOLDER of a lane kind — the field asked for most often.
|
|
732
752
|
_aw_lane_root() { _aw_lane_field "$1" root; }
|
|
733
753
|
|
|
734
754
|
# Every ecosystem the declared lanes belong to, one per line, deduped and empty when
|
|
@@ -815,10 +835,10 @@ cmd_lane_resolve() {
|
|
|
815
835
|
|
|
816
836
|
# The gate: is the declaration USABLE for the phase about to run. --require names a
|
|
817
837
|
# lane kind the phase cannot run without and is repeatable; every DECLARED lane is
|
|
818
|
-
# checked for a sound
|
|
838
|
+
# checked for a sound lane root folder whether or not it was required, because a stack
|
|
819
839
|
# named against a directory that is not there is a mistake in every phase.
|
|
820
|
-
# --allow-missing-root is the greenfield state, where select-stack declares a
|
|
821
|
-
#
|
|
840
|
+
# --allow-missing-root is the greenfield state, where select-stack declares a lane root
|
|
841
|
+
# folder before implement creates it.
|
|
822
842
|
cmd_lane_verify() {
|
|
823
843
|
local -a fwd=()
|
|
824
844
|
while [ $# -gt 0 ]; do
|
|
@@ -949,6 +969,13 @@ PY
|
|
|
949
969
|
# file; a new one is created. The context directory is created but domain-model.md is
|
|
950
970
|
# NOT — that document is authored by specify, which is the only writer of the model,
|
|
951
971
|
# and an empty placeholder would read as "the model exists" to every later gate.
|
|
972
|
+
#
|
|
973
|
+
# The active feature belongs to the context it was created in. When the selected
|
|
974
|
+
# context does not contain it, .appweaver/feature.json is cleared, so the next
|
|
975
|
+
# feature-scoped command stops with "No active feature" instead of writing into the
|
|
976
|
+
# context the developer just left (and adopt with no argument does not recreate that
|
|
977
|
+
# feature's name in the new context). A flat specs/<Feature> is outside every context
|
|
978
|
+
# and is cleared the same way.
|
|
952
979
|
cmd_set_context() {
|
|
953
980
|
local raw=""
|
|
954
981
|
while [ $# -gt 0 ]; do
|
|
@@ -991,11 +1018,20 @@ cmd_set_context() {
|
|
|
991
1018
|
"$(json_escape "$context")" "$(json_escape "$context_dir_rel")" > "$cj"
|
|
992
1019
|
fi
|
|
993
1020
|
|
|
1021
|
+
local active_fd; active_fd=$(read_feature_json_feature_directory "$repo_root")
|
|
1022
|
+
active_fd="${active_fd#"$repo_root/"}"; active_fd="${active_fd#./}"; active_fd="${active_fd%/}"
|
|
1023
|
+
local feature_cleared=false cleared_feature=""
|
|
1024
|
+
if [ -n "$active_fd" ] && [[ "$active_fd" != "$context_dir_rel/"* ]]; then
|
|
1025
|
+
_persist_feature_json "$repo_root" ""
|
|
1026
|
+
feature_cleared=true; cleared_feature="$active_fd"
|
|
1027
|
+
fi
|
|
1028
|
+
|
|
994
1029
|
local created=true; [ "$existed" = true ] && created=false
|
|
995
1030
|
local dm_exists=false; [ -f "$context_dir/domain-model.md" ] && dm_exists=true
|
|
996
|
-
printf '{"context":"%s","context_dir":"%s","domain_model":"%s","existed":%s,"created":%s,"domain_model_exists":%s,"written":true}\n' \
|
|
1031
|
+
printf '{"context":"%s","context_dir":"%s","domain_model":"%s","existed":%s,"created":%s,"domain_model_exists":%s,"feature_cleared":%s,"cleared_feature":"%s","written":true}\n' \
|
|
997
1032
|
"$(json_escape "$context")" "$(json_escape "$context_dir_rel")" \
|
|
998
|
-
"$(json_escape "$context_dir_rel/domain-model.md")" "$existed" "$created" "$dm_exists"
|
|
1033
|
+
"$(json_escape "$context_dir_rel/domain-model.md")" "$existed" "$created" "$dm_exists" \
|
|
1034
|
+
"$feature_cleared" "$(json_escape "$cleared_feature")"
|
|
999
1035
|
}
|
|
1000
1036
|
|
|
1001
1037
|
# ══════════════════════ domain model — context-level helpers ════════════════
|
|
@@ -1460,6 +1496,47 @@ PY
|
|
|
1460
1496
|
"$(json_escape "$context")"
|
|
1461
1497
|
}
|
|
1462
1498
|
|
|
1499
|
+
# ─────────────────────────────── default-scope ─────────────────────────────
|
|
1500
|
+
# The bounded context and feature a new project starts in, so a simple project goes
|
|
1501
|
+
# from /appweaver constitution straight to specify. Acts only on a project with no specs
|
|
1502
|
+
# yet: no directory under specs/ (no context, no feature) and no active feature. Every
|
|
1503
|
+
# other state is reported as skipped and left untouched, so a re-run of constitution
|
|
1504
|
+
# never moves the active feature and an onboarded brownfield tree is never added to.
|
|
1505
|
+
# Creation goes through set-context and set-feature, so the folders and both pointer
|
|
1506
|
+
# files are written exactly as select-bounded-context and select-feature write them.
|
|
1507
|
+
# context_dir / feature_dir report the active pair after the call, created or not.
|
|
1508
|
+
cmd_default_scope() {
|
|
1509
|
+
local ctx="" feat=""
|
|
1510
|
+
while [ $# -gt 0 ]; do
|
|
1511
|
+
case "$1" in
|
|
1512
|
+
--context) ctx="$2"; shift 2 ;;
|
|
1513
|
+
--feature) feat="$2"; shift 2 ;;
|
|
1514
|
+
--help|-h) echo "default-scope --context <name> --feature <name> (create and activate both on a project with no specs yet)"; return 0 ;;
|
|
1515
|
+
*) echo "default-scope: unknown arg '$1'" >&2; return 1 ;;
|
|
1516
|
+
esac
|
|
1517
|
+
done
|
|
1518
|
+
{ [ -n "$ctx" ] && [ -n "$feat" ]; } || { echo "default-scope: --context and --feature are required" >&2; return 1; }
|
|
1519
|
+
|
|
1520
|
+
local repo_root; repo_root=$(get_repo_root)
|
|
1521
|
+
local reason=""
|
|
1522
|
+
if [ -d "$repo_root/specs" ] && [ -n "$(find "$repo_root/specs" -mindepth 1 -maxdepth 1 -type d -print -quit 2>/dev/null)" ]; then
|
|
1523
|
+
reason="specs/ already holds a bounded context or a feature"
|
|
1524
|
+
elif [ -n "$(read_feature_json_feature_directory "$repo_root")" ]; then
|
|
1525
|
+
reason="a feature is already active in .appweaver/feature.json"
|
|
1526
|
+
fi
|
|
1527
|
+
|
|
1528
|
+
local created=false
|
|
1529
|
+
if [ -z "$reason" ]; then
|
|
1530
|
+
cmd_set_context --context "$ctx" >/dev/null || return 1
|
|
1531
|
+
cmd_set_feature --feature "$feat" >/dev/null || return 1
|
|
1532
|
+
created=true
|
|
1533
|
+
fi
|
|
1534
|
+
printf '{"created":%s,"skipped_reason":"%s","context_dir":"%s","feature_dir":"%s"}\n' \
|
|
1535
|
+
"$created" "$(json_escape "$reason")" \
|
|
1536
|
+
"$(json_escape "$(read_context_json_context_directory "$repo_root")")" \
|
|
1537
|
+
"$(json_escape "$(read_feature_json_feature_directory "$repo_root")")"
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1463
1540
|
# ═══════════════════════════ active_version.json ════════════════════════════
|
|
1464
1541
|
# PER-FEATURE state at <feature_dir>/active_version.json:
|
|
1465
1542
|
# {feature, version, active_change, updated}
|
|
@@ -1938,7 +2015,7 @@ cmd_detect_project() {
|
|
|
1938
2015
|
local repo_root; repo_root=$(get_repo_root)
|
|
1939
2016
|
local -a markers=()
|
|
1940
2017
|
local scope="repo" start="$repo_root" rel
|
|
1941
|
-
# A lane
|
|
2018
|
+
# A lane root folder arrives as the value declare stored, so it may carry a
|
|
1942
2019
|
# trailing slash; an option label ("path — why") never reaches a script argument.
|
|
1943
2020
|
rel="${scope_root#./}"; rel="${rel%/}"
|
|
1944
2021
|
if [ -n "$rel" ] && [ -d "$repo_root/$rel" ]; then
|
|
@@ -1971,10 +2048,12 @@ cmd_detect_project() {
|
|
|
1971
2048
|
# they scaffold. Names are unique across stacks, so callers pass a bare <Name> and
|
|
1972
2049
|
# the lookup searches every stack.
|
|
1973
2050
|
|
|
1974
|
-
# Echo "<stack>/<Name>" for each foundation directory, one per line
|
|
1975
|
-
#
|
|
1976
|
-
#
|
|
1977
|
-
# foundations in
|
|
2051
|
+
# Echo "<stack>/<Name>" for each PACK foundation directory, one per line — the scan
|
|
2052
|
+
# foundation-locate and foundation-unpack resolve a pack name through. A stack also holds
|
|
2053
|
+
# siblings that are not foundations (components/), so carrying a *.pack.txt is what makes
|
|
2054
|
+
# a directory one here — without that test they surface as bundle-less foundations in
|
|
2055
|
+
# every lookup. A directory carrying only *.spec.txt is listed by foundation-list
|
|
2056
|
+
# (aw-foundation.py) and never unpacked, so it is deliberately absent from this scan.
|
|
1978
2057
|
_foundation_dirs() {
|
|
1979
2058
|
local repo_root="$1" s d
|
|
1980
2059
|
[ -d "$repo_root/foundations" ] || return 0
|
|
@@ -1997,98 +2076,145 @@ _foundation_stack() {
|
|
|
1997
2076
|
}
|
|
1998
2077
|
|
|
1999
2078
|
# ──────────────────────────── foundation-list ──────────────────────────────
|
|
2000
|
-
#
|
|
2001
|
-
# <Name>.pack.txt)
|
|
2002
|
-
#
|
|
2003
|
-
#
|
|
2004
|
-
#
|
|
2079
|
+
# What ONE lane can be scaffolded from, as a pick list — deterministic, no LLM. Every
|
|
2080
|
+
# pack (<Name>.pack.txt) and every spec (*.spec.txt) under foundations/<ecosystem>/<Name>/
|
|
2081
|
+
# that fits the lane, then "[manual spec]" and "[skip]". The scan, the header filters
|
|
2082
|
+
# (# kind / # profiles) and the option labels live in aw-foundation.py so the two shells
|
|
2083
|
+
# answer from one implementation.
|
|
2084
|
+
#
|
|
2085
|
+
# WHICH entries a lane may be offered. A pack is a whole compiling project in one
|
|
2086
|
+
# ecosystem's build system, and an ecosystem is shared by stacks that cannot use each
|
|
2087
|
+
# other's scaffold, so three filters apply: --ecosystem (the lane's), --kind (backend |
|
|
2088
|
+
# frontend; an entry with no `# kind:` is backend) and --profile (only when the entry
|
|
2089
|
+
# names profiles). Without --ecosystem the filter falls back to every declared lane's
|
|
2090
|
+
# ecosystem, and to nothing at all on a bare repo; an EMPTY --ecosystem filters to none.
|
|
2005
2091
|
#
|
|
2006
|
-
#
|
|
2007
|
-
#
|
|
2008
|
-
#
|
|
2009
|
-
#
|
|
2010
|
-
# a path argument that is quietly dropped is how someone believes they scaffolded a
|
|
2011
|
-
# sandbox and finds they overwrote their service.
|
|
2092
|
+
# --args "<Foundation> <App>" is the direct-argument fast path: preselect_option is the
|
|
2093
|
+
# option for that name when this lane offers it; preselect_app carries the 2nd token. A
|
|
2094
|
+
# THIRD token comes back as `rejected_out` — a scaffold lands in its lane root folder,
|
|
2095
|
+
# so there is no caller-chosen destination to accept, and the workflow refuses it by name.
|
|
2012
2096
|
cmd_foundation_list() {
|
|
2013
|
-
local
|
|
2097
|
+
local -a fwd=()
|
|
2098
|
+
local eco_given=false
|
|
2014
2099
|
while [ $# -gt 0 ]; do
|
|
2015
2100
|
case "$1" in
|
|
2016
|
-
--args)
|
|
2017
|
-
--ecosystem)
|
|
2018
|
-
--help|-h) echo "foundation-list [--
|
|
2101
|
+
--args|--kind|--profile|--lane-id) fwd+=("$1" "$2"); shift 2 ;;
|
|
2102
|
+
--ecosystem) fwd+=(--ecosystem "$2"); eco_given=true; shift 2 ;;
|
|
2103
|
+
--help|-h) echo "foundation-list [--kind backend|frontend] [--profile <id>] [--ecosystem <eco>] [--lane-id <id>] [--args '<Foundation> <App>'] -> {options[], entries[], count, foundations[], specs[], all_foundations[], preselect_option, preselect_name, preselect_app, rejected_out}"; return 0 ;;
|
|
2019
2104
|
*) echo "foundation-list: unknown arg '$1'" >&2; return 1 ;;
|
|
2020
2105
|
esac
|
|
2021
2106
|
done
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
# compiling project in one ecosystem's languages and build system, so unpacking
|
|
2026
|
-
# a .NET one into a Spring repo does not produce a scaffold — it produces a
|
|
2027
|
-
# second, foreign project beside the real one.
|
|
2028
|
-
#
|
|
2029
|
-
# --ecosystem narrows to the ONE lane being scaffolded, which is stricter than the
|
|
2030
|
-
# union of every declared lane: a repo with a .NET service and a Node UI must not be
|
|
2031
|
-
# offered its .NET bundle while the UI lane is the target. Without it the filter falls
|
|
2032
|
-
# back to every declared lane's ecosystem, and to nothing at all on a bare repo.
|
|
2033
|
-
local -a lane_ecos=()
|
|
2034
|
-
local e
|
|
2035
|
-
if [ -n "$eco_filter" ]; then
|
|
2036
|
-
lane_ecos=("$eco_filter")
|
|
2037
|
-
else
|
|
2038
|
-
while IFS= read -r e; do [ -n "$e" ] && lane_ecos+=("$e"); done < <(_aw_ecosystems)
|
|
2107
|
+
if [ "$eco_given" = false ]; then
|
|
2108
|
+
local e
|
|
2109
|
+
while IFS= read -r e; do [ -n "$e" ] && fwd+=(--ecosystem "$e"); done < <(_aw_ecosystems)
|
|
2039
2110
|
fi
|
|
2111
|
+
_aw_foundation_py list "${fwd[@]}"
|
|
2112
|
+
}
|
|
2040
2113
|
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
done
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2114
|
+
# ─────────────────────────── foundation-choice ─────────────────────────────
|
|
2115
|
+
# Resolve the option picked from foundation-list back to its entry: route (pack | spec |
|
|
2116
|
+
# manual | skip), name and file. Takes the same filters as the list, so a numbered answer
|
|
2117
|
+
# counts the same rows the operator saw.
|
|
2118
|
+
cmd_foundation_choice() {
|
|
2119
|
+
local -a fwd=()
|
|
2120
|
+
while [ $# -gt 0 ]; do
|
|
2121
|
+
case "$1" in
|
|
2122
|
+
--selection|--kind|--profile|--ecosystem) fwd+=("$1" "$2"); shift 2 ;;
|
|
2123
|
+
--help|-h) echo "foundation-choice --selection <option> [--kind <k>] [--profile <id>] [--ecosystem <eco>] -> {ok, route, name, file, ecosystem, title, reason}"; return 0 ;;
|
|
2124
|
+
*) echo "foundation-choice: unknown arg '$1'" >&2; return 1 ;;
|
|
2125
|
+
esac
|
|
2126
|
+
done
|
|
2127
|
+
_aw_foundation_py choice "${fwd[@]}"
|
|
2128
|
+
}
|
|
2129
|
+
|
|
2130
|
+
# ────────────────────────── foundation-spec-capture ────────────────────────
|
|
2131
|
+
# Copy the spec a lane is about to be built from to .appweaver/foundation/<lane>.spec.md
|
|
2132
|
+
# — from a bundle's *.spec.txt (--file), or from what the operator gave (--input: a path
|
|
2133
|
+
# when it names a file, the spec text otherwise). Always exits 0; `ok` carries the verdict.
|
|
2134
|
+
cmd_foundation_spec_capture() {
|
|
2135
|
+
local -a fwd=()
|
|
2136
|
+
while [ $# -gt 0 ]; do
|
|
2137
|
+
case "$1" in
|
|
2138
|
+
--lane|--kind|--file|--input) fwd+=("$1" "$2"); shift 2 ;;
|
|
2139
|
+
--help|-h) echo "foundation-spec-capture --lane <id> [--kind <k>] (--file <path> | --input <path|text>) -> {ok, spec_path, source, chars, title, reason}"; return 0 ;;
|
|
2140
|
+
*) echo "foundation-spec-capture: unknown arg '$1'" >&2; return 1 ;;
|
|
2141
|
+
esac
|
|
2142
|
+
done
|
|
2143
|
+
_aw_foundation_py spec-capture "${fwd[@]}"
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
# ─────────────────────────── foundation-lane-files ─────────────────────────
|
|
2147
|
+
# What a lane already holds: its lane root folder, its test path when that is another
|
|
2148
|
+
# subtree, and its build files where its commands run when that is outside the lane (the
|
|
2149
|
+
# profile's `detect` entries with no `/`). Build output and dependency directories are
|
|
2150
|
+
# named, not walked. Asked before anything is scaffolded into the lane.
|
|
2151
|
+
cmd_foundation_lane_files() {
|
|
2152
|
+
local -a fwd=()
|
|
2153
|
+
while [ $# -gt 0 ]; do
|
|
2154
|
+
case "$1" in
|
|
2155
|
+
--source|--test|--workdir|--profile) fwd+=("$1" "$2"); shift 2 ;;
|
|
2156
|
+
--help|-h) echo "foundation-lane-files --source <p> [--test <p>] [--workdir <p>] [--profile <id>] -> {has_files, count, sample[], generated[], root_files[], folders[], workdir, summary}"; return 0 ;;
|
|
2157
|
+
*) echo "foundation-lane-files: unknown arg '$1'" >&2; return 1 ;;
|
|
2158
|
+
esac
|
|
2159
|
+
done
|
|
2160
|
+
_aw_foundation_py lane-files "${fwd[@]}"
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
# ─────────────────────── foundation-snapshot / -outside ────────────────────
|
|
2164
|
+
# A spec build is told to write only inside its lane root folder; these check that it did.
|
|
2165
|
+
# `snapshot` records every file in the repo before the build; `outside` lists what was
|
|
2166
|
+
# created, changed or deleted anywhere else afterwards and, with --undo, removes what was
|
|
2167
|
+
# created. Build output and dependency folders are one entry each, not walked.
|
|
2168
|
+
cmd_foundation_snapshot() {
|
|
2169
|
+
local -a fwd=()
|
|
2170
|
+
while [ $# -gt 0 ]; do
|
|
2171
|
+
case "$1" in
|
|
2172
|
+
--out) fwd+=("$1" "$2"); shift 2 ;;
|
|
2173
|
+
--help|-h) echo "foundation-snapshot --out <repo-relative file> -> {ok, snapshot, entries}"; return 0 ;;
|
|
2174
|
+
*) echo "foundation-snapshot: unknown arg '$1'" >&2; return 1 ;;
|
|
2175
|
+
esac
|
|
2176
|
+
done
|
|
2177
|
+
_aw_foundation_py snapshot "${fwd[@]}"
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
cmd_foundation_outside() {
|
|
2181
|
+
local -a fwd=()
|
|
2182
|
+
while [ $# -gt 0 ]; do
|
|
2183
|
+
case "$1" in
|
|
2184
|
+
--snapshot|--lane) fwd+=("$1" "$2"); shift 2 ;;
|
|
2185
|
+
--undo) fwd+=("$1"); shift ;;
|
|
2186
|
+
--help|-h) echo "foundation-outside --snapshot <file> --lane <lane root folder> [--undo] -> {clean, created[], changed[], deleted[], removed[], summary}"; return 0 ;;
|
|
2187
|
+
*) echo "foundation-outside: unknown arg '$1'" >&2; return 1 ;;
|
|
2188
|
+
esac
|
|
2189
|
+
done
|
|
2190
|
+
_aw_foundation_py outside "${fwd[@]}"
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
_aw_foundation_py() {
|
|
2194
|
+
local repo_root; repo_root=$(get_repo_root)
|
|
2195
|
+
local core="$SCRIPT_DIR/../python/aw-foundation.py"
|
|
2196
|
+
if [ ! -f "$core" ]; then
|
|
2197
|
+
echo "aw-foundation: core not found at $core" >&2; return 1
|
|
2198
|
+
fi
|
|
2199
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
2200
|
+
echo "aw-foundation: python3 is required to list foundations" >&2; return 1
|
|
2060
2201
|
fi
|
|
2061
|
-
|
|
2062
|
-
local match="" f
|
|
2063
|
-
for f in "${foundations[@]}"; do [ "$f" = "$pre_name" ] && match="$pre_name"; done
|
|
2064
|
-
printf '{"foundations":%s,"count":%s,"all_foundations":%s,"all_count":%s,"ecosystems":%s,"lane_ecosystems":%s,"filtered":%s,"preselect_name":"%s","preselect_app":"%s","rejected_out":"%s"}\n' \
|
|
2065
|
-
"$(json_str_array "${foundations[@]}")" "${#foundations[@]}" \
|
|
2066
|
-
"$(json_str_array "${all[@]}")" "${#all[@]}" \
|
|
2067
|
-
"$(json_str_array "${eco_of[@]}")" "$(json_str_array "${lane_ecos[@]}")" \
|
|
2068
|
-
"$([ ${#lane_ecos[@]} -gt 0 ] && echo true || echo false)" \
|
|
2069
|
-
"$(json_escape "$match")" "$(json_escape "$pre_app")" "$(json_escape "$rejected_out")"
|
|
2202
|
+
python3 "$core" --repo "$repo_root" "$@"
|
|
2070
2203
|
}
|
|
2071
2204
|
|
|
2072
2205
|
# ─────────────────────────── foundation-locate ─────────────────────────────
|
|
2073
2206
|
# Parse/validate args, locate the pack + unpacker, read the pack header (bounded).
|
|
2074
2207
|
cmd_foundation_locate() {
|
|
2075
|
-
local name="" app="" out="."
|
|
2208
|
+
local name="" app="" out="."
|
|
2076
2209
|
while [ $# -gt 0 ]; do
|
|
2077
2210
|
case "$1" in
|
|
2078
2211
|
--name) name="$2"; shift 2 ;;
|
|
2079
2212
|
--app) app="$2"; shift 2 ;;
|
|
2080
2213
|
--out) out="$2"; shift 2 ;;
|
|
2081
|
-
--
|
|
2082
|
-
--help|-h) echo "foundation-locate (--args '<F> <A> [out]' | --name <F> --app <A> [--out <dir>])"; return 0 ;;
|
|
2214
|
+
--help|-h) echo "foundation-locate --name <F> --app <A> [--out <dir>]"; return 0 ;;
|
|
2083
2215
|
*) echo "foundation-locate: unknown arg '$1'" >&2; return 1 ;;
|
|
2084
2216
|
esac
|
|
2085
2217
|
done
|
|
2086
|
-
# positional parse: "<FoundationName> <AppName> [OutputDir]"
|
|
2087
|
-
if [ -n "$raw" ]; then
|
|
2088
|
-
# shellcheck disable=SC2086
|
|
2089
|
-
set -- $raw
|
|
2090
|
-
[ -n "$1" ] && name="$1"; [ -n "$2" ] && app="$2"; [ -n "$3" ] && out="$3"
|
|
2091
|
-
fi
|
|
2092
2218
|
local repo_root; repo_root=$(get_repo_root)
|
|
2093
2219
|
# available = every foundation dir (_foundation_dirs already requires a *.pack.txt)
|
|
2094
2220
|
local -a available=()
|
|
@@ -2098,7 +2224,7 @@ cmd_foundation_locate() {
|
|
|
2098
2224
|
done < <(_foundation_dirs "$repo_root")
|
|
2099
2225
|
_fnd_fail() { printf '{"ok":false,"reason":"%s","available":%s}\n' "$(json_escape "$1")" "$(json_str_array "${available[@]}")"; }
|
|
2100
2226
|
if [ -z "$name" ] || [ -z "$app" ]; then
|
|
2101
|
-
_fnd_fail "
|
|
2227
|
+
_fnd_fail "foundation-locate needs --name <FoundationName> and --app <AppName>"; return 0
|
|
2102
2228
|
fi
|
|
2103
2229
|
local stack; stack=$(_foundation_stack "$repo_root" "$name")
|
|
2104
2230
|
if [ -z "$stack" ]; then _fnd_fail "Foundation '$name' not found (expected foundations/<stack>/$name/)"; return 0; fi
|
|
@@ -2113,7 +2239,7 @@ cmd_foundation_locate() {
|
|
|
2113
2239
|
elif [ -f "$fdir/unpack-foundation.sh" ]; then
|
|
2114
2240
|
runner="sh"; unpacker="$rel/unpack-foundation.sh"
|
|
2115
2241
|
else
|
|
2116
|
-
|
|
2242
|
+
_fnd_fail "No unpacker found in $rel/ (unpack-foundation.ps1 or .sh)"; return 0
|
|
2117
2243
|
fi
|
|
2118
2244
|
# bounded header read (never load the pack body)
|
|
2119
2245
|
local header sentinel file_count
|
|
@@ -2122,17 +2248,21 @@ cmd_foundation_locate() {
|
|
|
2122
2248
|
file_count=$(printf '%s\n' "$header" | grep -iE 'file' | grep -oE '[0-9]+' | head -1)
|
|
2123
2249
|
[ -n "$file_count" ] || file_count="0"
|
|
2124
2250
|
|
|
2125
|
-
|
|
2251
|
+
# A reference sample is offered only when its pack is there to unpack.
|
|
2252
|
+
local has_reference=false
|
|
2253
|
+
[ -f "$fdir/$name.Reference.pack.txt" ] && has_reference=true
|
|
2254
|
+
|
|
2255
|
+
printf '{"ok":true,"reason":"","name":"%s","stack":"%s","app":"%s","out_dir":"%s","pack_file":"%s","unpacker":"%s","runner":"%s","sentinel":"%s","file_count":%s,"has_reference":%s,"available":%s}\n' \
|
|
2126
2256
|
"$(json_escape "$name")" "$(json_escape "$stack")" "$(json_escape "$app")" "$(json_escape "$out")" "$(json_escape "$rel/$name.pack.txt")" \
|
|
2127
|
-
"$(json_escape "$unpacker")" "$runner" "$(json_escape "$sentinel")" "$file_count" "$(json_str_array "${available[@]}")"
|
|
2257
|
+
"$(json_escape "$unpacker")" "$runner" "$(json_escape "$sentinel")" "$file_count" "$has_reference" "$(json_str_array "${available[@]}")"
|
|
2128
2258
|
}
|
|
2129
2259
|
|
|
2130
2260
|
# ─────────────────────────── foundation-unpack ─────────────────────────────
|
|
2131
2261
|
# Run the OS-selected unpacker for the foundation (or reference) pack.
|
|
2132
2262
|
#
|
|
2133
|
-
# --out is the LANE
|
|
2263
|
+
# --out is the LANE ROOT FOLDER, so every file a pack carries lands inside the subtree
|
|
2134
2264
|
# its lane owns. --test-path is the lane's other subtree: a pack that carries its own test
|
|
2135
|
-
# project puts it under the
|
|
2265
|
+
# project puts it under the lane root folder like everything else, and aw-foundation.py lifts it
|
|
2136
2266
|
# onto the path the lane actually keeps tests in. See that module for when it is a no-op.
|
|
2137
2267
|
cmd_foundation_unpack() {
|
|
2138
2268
|
local name="" app="" out="." dry=false force="no" reference=false test_path=""
|
|
@@ -2206,27 +2336,35 @@ cmd_foundation_unpack() {
|
|
|
2206
2336
|
"$(json_escape "$(printf '%s' "$outp" | tail -c 800)")"
|
|
2207
2337
|
}
|
|
2208
2338
|
|
|
2209
|
-
#
|
|
2210
|
-
#
|
|
2211
|
-
#
|
|
2212
|
-
|
|
2213
|
-
|
|
2339
|
+
# ───────────────────── foundation-rules / -rules-done ──────────────────────
|
|
2340
|
+
# A pack's rule file (<pack name>.rules.json beside it) names the files and folders that
|
|
2341
|
+
# are merged with, or replaced over, what the lane already holds; every other pack file is
|
|
2342
|
+
# written as packed. `rules` runs before an unpack: it lists the merges and puts the files
|
|
2343
|
+
# they merge with aside under --state; `rules-done` removes them once merged. Both
|
|
2344
|
+
# delegate to aw-foundation.py.
|
|
2345
|
+
cmd_foundation_rules() {
|
|
2346
|
+
local -a fwd=()
|
|
2214
2347
|
while [ $# -gt 0 ]; do
|
|
2215
2348
|
case "$1" in
|
|
2216
|
-
--out)
|
|
2217
|
-
--
|
|
2218
|
-
|
|
2349
|
+
--name|--app|--out|--state) fwd+=("$1" "$2"); shift 2 ;;
|
|
2350
|
+
--reference) fwd+=("$1"); shift ;;
|
|
2351
|
+
--help|-h) echo "foundation-rules --name <F> --app <A> --out <lane root folder> [--reference] [--state <folder>] -> {ok, reason, rules_file, rules, merges[], merge_count, stale[], note}"; return 0 ;;
|
|
2352
|
+
*) echo "foundation-rules: unknown arg '$1'" >&2; return 1 ;;
|
|
2219
2353
|
esac
|
|
2220
2354
|
done
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
local
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2355
|
+
_aw_foundation_py rules "${fwd[@]}"
|
|
2356
|
+
}
|
|
2357
|
+
|
|
2358
|
+
cmd_foundation_rules_done() {
|
|
2359
|
+
local -a fwd=()
|
|
2360
|
+
while [ $# -gt 0 ]; do
|
|
2361
|
+
case "$1" in
|
|
2362
|
+
--state) fwd+=("$1" "$2"); shift 2 ;;
|
|
2363
|
+
--help|-h) echo "foundation-rules-done [--state <folder>] -> {ok, removed}"; return 0 ;;
|
|
2364
|
+
*) echo "foundation-rules-done: unknown arg '$1'" >&2; return 1 ;;
|
|
2365
|
+
esac
|
|
2366
|
+
done
|
|
2367
|
+
_aw_foundation_py rules-done "${fwd[@]}"
|
|
2230
2368
|
}
|
|
2231
2369
|
|
|
2232
2370
|
# ─────────────────────────── foundation-verify ─────────────────────────────
|
|
@@ -2246,9 +2384,11 @@ cmd_foundation_verify() {
|
|
|
2246
2384
|
local -a residue=()
|
|
2247
2385
|
if [ -n "$sentinel" ] && [ -d "$out" ]; then
|
|
2248
2386
|
# scan the generated tree only — never the bundle/framework dirs (the
|
|
2249
|
-
# .pack.txt legitimately contains the sentinel).
|
|
2387
|
+
# .pack.txt legitimately contains the sentinel). -F: the sentinel is literal
|
|
2388
|
+
# text, and as a pattern its dots match any character ("AppWeaver Foo" would
|
|
2389
|
+
# count as AppWeaver.Foo) — the PowerShell twin uses -SimpleMatch for the same.
|
|
2250
2390
|
while IFS= read -r m; do [ -n "$m" ] && residue+=("$m"); done < <(
|
|
2251
|
-
grep -
|
|
2391
|
+
grep -rIlF --exclude-dir=.git --exclude-dir=guardrails --exclude-dir=foundations --exclude-dir=.appweaver \
|
|
2252
2392
|
--exclude-dir=specs --exclude-dir=node_modules -- "$sentinel" "$out" 2>/dev/null \
|
|
2253
2393
|
| sed "s#^$repo_root/##" | head -50 )
|
|
2254
2394
|
fi
|
|
@@ -2743,7 +2883,7 @@ cmd_find_host_project() {
|
|
|
2743
2883
|
done
|
|
2744
2884
|
local repo_root; repo_root=$(get_repo_root)
|
|
2745
2885
|
local prog
|
|
2746
|
-
# WHERE the host lives. The
|
|
2886
|
+
# WHERE the host lives. The declared lane root folder of the service lane when there is one,
|
|
2747
2887
|
# `src/` when there is not — the same question run-migrate asks, and the same answer.
|
|
2748
2888
|
# A repo whose service sits in `backend/` or `Legacy/App/` is searched where its code
|
|
2749
2889
|
# actually is instead of being reported as having no host project at all.
|
|
@@ -3061,10 +3201,15 @@ except Exception:
|
|
|
3061
3201
|
}
|
|
3062
3202
|
|
|
3063
3203
|
cmd_build_compile() {
|
|
3204
|
+
# --kind builds ONE declared lane — what `foundation` uses to check the lane it just
|
|
3205
|
+
# created without compiling a lane it did not touch. Ignored on the undeclared ladder
|
|
3206
|
+
# below, which has no lanes to choose between.
|
|
3207
|
+
local kind=""
|
|
3064
3208
|
while [ $# -gt 0 ]; do
|
|
3065
3209
|
case "$1" in
|
|
3066
3210
|
--feature-dir) shift 2 ;;
|
|
3067
|
-
--
|
|
3211
|
+
--kind) kind="$2"; shift 2 ;;
|
|
3212
|
+
--help|-h) echo "build-compile [--feature-dir <path>] [--kind backend|frontend]"; return 0 ;;
|
|
3068
3213
|
*) echo "build-compile: unknown arg '$1'" >&2; return 1 ;;
|
|
3069
3214
|
esac
|
|
3070
3215
|
done
|
|
@@ -3074,7 +3219,11 @@ cmd_build_compile() {
|
|
|
3074
3219
|
# sniffer takes the first toolchain it finds and silently never builds the
|
|
3075
3220
|
# second, which reports green for a frontend that was never compiled.
|
|
3076
3221
|
if [ -n "$(_aw_lanes_declared)" ]; then
|
|
3077
|
-
|
|
3222
|
+
if [ -n "$kind" ]; then
|
|
3223
|
+
_aw_stack_py run-phase --phase build --kind "$kind"
|
|
3224
|
+
else
|
|
3225
|
+
_aw_stack_py run-phase --phase build
|
|
3226
|
+
fi
|
|
3078
3227
|
return $?
|
|
3079
3228
|
fi
|
|
3080
3229
|
|
|
@@ -3283,8 +3432,8 @@ print(json.dumps({"ok": bool(d.get("ok")), "tool": "lanes", "ran": bool(d.get("r
|
|
|
3283
3432
|
return 0
|
|
3284
3433
|
fi
|
|
3285
3434
|
|
|
3286
|
-
# WHERE to look for the migration project. The
|
|
3287
|
-
#
|
|
3435
|
+
# WHERE to look for the migration project. The declared lane root folder of the
|
|
3436
|
+
# service lane when there is one, `src/` when there is not. Migrations belong to the
|
|
3288
3437
|
# backend lane by definition, so a repo whose service code sits in `Legacy/App/`
|
|
3289
3438
|
# or `backend/` gets searched where its code actually is instead of being told
|
|
3290
3439
|
# it has no .NET project at all.
|
|
@@ -3297,7 +3446,7 @@ print(json.dumps({"ok": bool(d.get("ok")), "tool": "lanes", "ran": bool(d.get("r
|
|
|
3297
3446
|
if ls "$repo_root"/*.sln >/dev/null 2>&1 || find "$svc_dir" -maxdepth 4 -name "*.csproj" 2>/dev/null | grep -q .; then has_dotnet=true; fi
|
|
3298
3447
|
if [ "$has_dotnet" = true ] && command -v dotnet >/dev/null 2>&1 && ( cd "$repo_root" && dotnet ef --version >/dev/null 2>&1 ); then
|
|
3299
3448
|
# Prefer *Service.csproj (API host with ApplicationDbContext); fall back to first
|
|
3300
|
-
# non-AppHost/Web/ServiceDefaults/test project under the service lane
|
|
3449
|
+
# non-AppHost/Web/ServiceDefaults/test project under the service lane root folder.
|
|
3301
3450
|
local project=""
|
|
3302
3451
|
project=$(find "$svc_dir" -maxdepth 3 -name '*.Service.csproj' 2>/dev/null | head -1)
|
|
3303
3452
|
if [ -z "$project" ]; then
|
|
@@ -5055,6 +5204,7 @@ case "$sub" in
|
|
|
5055
5204
|
set-context) cmd_set_context "$@" ;;
|
|
5056
5205
|
list-features) cmd_list_features "$@" ;;
|
|
5057
5206
|
set-feature) cmd_set_feature "$@" ;;
|
|
5207
|
+
default-scope) cmd_default_scope "$@" ;;
|
|
5058
5208
|
version-read) cmd_version_read "$@" ;;
|
|
5059
5209
|
version-init) cmd_version_init "$@" ;;
|
|
5060
5210
|
version-bootstrap-done) cmd_version_bootstrap_done "$@" ;;
|
|
@@ -5095,10 +5245,16 @@ case "$sub" in
|
|
|
5095
5245
|
checklist-apply) cmd_checklist_apply "$@" ;;
|
|
5096
5246
|
detect-project) cmd_detect_project "$@" ;;
|
|
5097
5247
|
foundation-list) cmd_foundation_list "$@" ;;
|
|
5248
|
+
foundation-choice) cmd_foundation_choice "$@" ;;
|
|
5249
|
+
foundation-spec-capture) cmd_foundation_spec_capture "$@" ;;
|
|
5250
|
+
foundation-lane-files) cmd_foundation_lane_files "$@" ;;
|
|
5251
|
+
foundation-snapshot) cmd_foundation_snapshot "$@" ;;
|
|
5252
|
+
foundation-outside) cmd_foundation_outside "$@" ;;
|
|
5098
5253
|
foundation-locate) cmd_foundation_locate "$@" ;;
|
|
5099
5254
|
foundation-unpack) cmd_foundation_unpack "$@" ;;
|
|
5100
5255
|
foundation-verify) cmd_foundation_verify "$@" ;;
|
|
5101
|
-
foundation-
|
|
5256
|
+
foundation-rules) cmd_foundation_rules "$@" ;;
|
|
5257
|
+
foundation-rules-done) cmd_foundation_rules_done "$@" ;;
|
|
5102
5258
|
list-components) cmd_list_components "$@" ;;
|
|
5103
5259
|
component-catalog) cmd_component_catalog "$@" ;;
|
|
5104
5260
|
component-verify) cmd_component_verify "$@" ;;
|