aw-spec-cli 0.1.8 → 0.1.9
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/DESIGN-FORMAT.md +200 -0
- package/assets/.appweaver/ORCH-DIALECT.md +156 -0
- package/assets/.appweaver/RULE-PARTITION.md +175 -0
- package/assets/.appweaver/STACK-PROFILES.md +461 -0
- package/assets/.appweaver/ai-rules.md +180 -0
- package/assets/.appweaver/domain.json +1 -0
- package/assets/.appweaver/extensions/.registry +19 -0
- package/assets/.appweaver/extensions/agent-context/README.md +57 -0
- package/assets/.appweaver/extensions/agent-context/agent-context-config.yml +4 -0
- package/assets/.appweaver/extensions/agent-context/commands/aw-spec.agent-context.update.md +26 -0
- package/assets/.appweaver/extensions/agent-context/extension.yml +37 -0
- package/assets/.appweaver/extensions/agent-context/scripts/bash/update-agent-context.sh +201 -0
- package/assets/.appweaver/extensions/agent-context/scripts/powershell/update-agent-context.ps1 +237 -0
- package/assets/.appweaver/extensions.yml +16 -0
- package/assets/.appweaver/feature.json +3 -0
- package/assets/.appweaver/init-options.json +9 -0
- package/assets/.appweaver/integration.json +15 -0
- package/assets/.appweaver/integrations/aw-spec.manifest.json +9 -0
- package/assets/.appweaver/interpreter/run-skill.py +1176 -0
- package/assets/.appweaver/memory/constitution.md +50 -0
- package/assets/.appweaver/orchestrator/.smoke-out/smoke.md +6 -0
- package/assets/.appweaver/orchestrator/AGENT-PROFILE.md +94 -0
- package/assets/.appweaver/orchestrator/README.md +300 -0
- package/assets/.appweaver/orchestrator/adapters/claude_agent_sdk.py +74 -0
- package/assets/.appweaver/orchestrator/adapters/google_antigravity.py +74 -0
- package/assets/.appweaver/orchestrator/modules/delta-design.orch.yaml +105 -0
- package/assets/.appweaver/orchestrator/modules/delta-spec.orch.yaml +101 -0
- package/assets/.appweaver/orchestrator/modules/domain-select.orch.yaml +90 -0
- package/assets/.appweaver/orchestrator/modules/lane-gate.orch.yaml +126 -0
- package/assets/.appweaver/orchestrator/modules/tech-select.orch.yaml +123 -0
- package/assets/.appweaver/orchestrator/orchestrator.py +1721 -0
- package/assets/.appweaver/orchestrator/rev-templates/ai-rules.reference.md +40 -0
- package/assets/.appweaver/orchestrator/rev-templates/delta-design-template.skeleton.md +101 -0
- package/assets/.appweaver/orchestrator/rev-templates/design-template.skeleton.md +245 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-constitution.skeleton.md +63 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-requirements.skeleton.md +48 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-bindings.skeleton.md +59 -0
- package/assets/.appweaver/orchestrator/rev-templates/domain-rule-file.skeleton.md +39 -0
- package/assets/.appweaver/orchestrator/rev-templates/frontend-rule-file.skeleton.md +63 -0
- package/assets/.appweaver/orchestrator/rev-templates/tech-rule-bindings.skeleton.md +116 -0
- package/assets/.appweaver/orchestrator/rev-templates/technical-constitution.skeleton.md +106 -0
- package/assets/.appweaver/orchestrator/rev-templates/technical-rule-file.skeleton.md +54 -0
- package/assets/.appweaver/orchestrator/rev-templates/ui-constitution.skeleton.md +125 -0
- package/assets/.appweaver/orchestrator/rev-templates/ui-delta-design-template.skeleton.md +123 -0
- package/assets/.appweaver/orchestrator/rev-templates/ui-design-template.skeleton.md +294 -0
- package/assets/.appweaver/orchestrator/workflows/_smoke.orch.yaml +160 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-add-component.orch.yaml +270 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-adopt.orch.yaml +934 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-delta-design.orch.yaml +251 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-design.orch.yaml +265 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-analyze-slice.orch.yaml +437 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-build.orch.yaml +197 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-characterize.orch.yaml +475 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-close-delta.orch.yaml +181 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-constitution.orch.yaml +727 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-demo.orch.yaml +100 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-dependency-tree.orch.yaml +69 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-deploy.orch.yaml +85 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design-ui.orch.yaml +386 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-design.orch.yaml +382 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-finalize-delta.orch.yaml +462 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-foundation.orch.yaml +265 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-delta.orch.yaml +253 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement-ui.orch.yaml +267 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-implement.orch.yaml +287 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-inventory.orch.yaml +238 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-onboard.orch.yaml +1822 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-parity.orch.yaml +293 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-run.orch.yaml +194 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-domain.orch.yaml +83 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-feature.orch.yaml +128 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-select-stack.orch.yaml +296 -0
- package/assets/.appweaver/orchestrator/workflows/aw-spec-specify.orch.yaml +439 -0
- package/assets/.appweaver/scripts/bash/aw-inventory.sh +17 -0
- package/assets/.appweaver/scripts/bash/aw-parity.sh +13 -0
- package/assets/.appweaver/scripts/bash/aw-rev-constitution.sh +13 -0
- package/assets/.appweaver/scripts/bash/aw-spec-actions.sh +2626 -0
- package/assets/.appweaver/scripts/bash/aw-xref.sh +15 -0
- package/assets/.appweaver/scripts/bash/common.sh +533 -0
- package/assets/.appweaver/scripts/powershell/aw-inventory.ps1 +17 -0
- package/assets/.appweaver/scripts/powershell/aw-parity.ps1 +13 -0
- package/assets/.appweaver/scripts/powershell/aw-rev-constitution.ps1 +13 -0
- package/assets/.appweaver/scripts/powershell/aw-spec-actions.ps1 +2232 -0
- package/assets/.appweaver/scripts/powershell/aw-xref.ps1 +15 -0
- package/assets/.appweaver/scripts/python/aw-component-catalog.py +121 -0
- package/assets/.appweaver/scripts/python/aw-inventory.py +1180 -0
- package/assets/.appweaver/scripts/python/aw-parity.py +380 -0
- package/assets/.appweaver/scripts/python/aw-rev-constitution.py +329 -0
- package/assets/.appweaver/scripts/python/aw-stack.py +1251 -0
- package/assets/.appweaver/scripts/python/aw-xref.py +1330 -0
- package/assets/.appweaver/scripts/python/validate_okf.py +352 -0
- package/assets/.appweaver/stack-profiles/angular.json +16 -0
- package/assets/.appweaver/stack-profiles/blazor-wasm.json +17 -0
- package/assets/.appweaver/stack-profiles/dotnet-webapi.json +15 -0
- package/assets/.appweaver/stack-profiles/go-chi.json +14 -0
- package/assets/.appweaver/stack-profiles/java-spring.json +16 -0
- package/assets/.appweaver/stack-profiles/nextjs.json +16 -0
- package/assets/.appweaver/stack-profiles/node-express.json +15 -0
- package/assets/.appweaver/stack-profiles/python-fastapi.json +16 -0
- package/assets/.appweaver/stack-profiles/react-vite.json +17 -0
- package/assets/.appweaver/stack-profiles/sveltekit.json +16 -0
- package/assets/.appweaver/stack-profiles/vue-vite.json +17 -0
- package/assets/.appweaver/stacks.json +3 -0
- package/assets/.appweaver/templates/constitution-domain-template.md +116 -0
- package/assets/.appweaver/templates/constitution-technical-template.md +108 -0
- package/assets/.appweaver/templates/constitution-ui-template.md +116 -0
- package/assets/.appweaver/templates/delta-design-template.md +69 -0
- package/assets/.appweaver/templates/delta-spec-template.md +63 -0
- package/assets/.appweaver/templates/design-template.md +383 -0
- package/assets/.appweaver/templates/okf-concept-template.md +114 -0
- package/assets/.appweaver/templates/parity-manifest-template.json +49 -0
- package/assets/.appweaver/templates/source-map-template.md +85 -0
- package/assets/.appweaver/templates/spec-template.md +309 -0
- package/assets/.appweaver/templates/ui-design-template.md +273 -0
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.Reference.pack.txt +25248 -0
- package/assets/foundations/dotnet/ModularMonolith/ModularMonolith.pack.txt +5726 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-foundation.ps1 +145 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-foundation.sh +196 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-reference-foundation.ps1 +157 -0
- package/assets/foundations/dotnet/ModularMonolith/pack-reference-foundation.sh +209 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-foundation.ps1 +126 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-foundation.sh +217 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-reference-foundation.ps1 +129 -0
- package/assets/foundations/dotnet/ModularMonolith/unpack-reference-foundation.sh +218 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.Reference.pack.txt +19318 -0
- package/assets/foundations/dotnet/VerticalSlice/VerticalSlice.pack.txt +2833 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-foundation.ps1 +145 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-foundation.sh +196 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-reference-foundation.ps1 +157 -0
- package/assets/foundations/dotnet/VerticalSlice/pack-reference-foundation.sh +209 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-foundation.ps1 +126 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-foundation.sh +217 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-reference-foundation.ps1 +129 -0
- package/assets/foundations/dotnet/VerticalSlice/unpack-reference-foundation.sh +218 -0
- package/assets/foundations/dotnet/components/Email/README.md +119 -0
- package/assets/foundations/dotnet/components/Email/ReferenceEndpoints/SendEmailEndpoint.cs +84 -0
- package/assets/foundations/dotnet/components/Email/integration.md +300 -0
- package/assets/foundations/dotnet/components/Jobs/README.md +113 -0
- package/assets/foundations/dotnet/components/Jobs/integration.md +259 -0
- package/assets/foundations/dotnet/components/QrGenerator/README.md +85 -0
- package/assets/foundations/dotnet/components/QrGenerator/ReferenceEndpoints/GenerateQrEndpoint.cs +57 -0
- package/assets/foundations/dotnet/components/QrGenerator/integration.md +230 -0
- package/assets/foundations/dotnet/components/README.md +74 -0
- package/assets/guardrails/README.md +222 -0
- package/assets/guardrails/domain/healthcare/ai-rules.domain.md +34 -0
- package/assets/guardrails/domain/healthcare/api-versioning.domain.md +17 -0
- package/assets/guardrails/domain/healthcare/auditing.domain.md +97 -0
- package/assets/guardrails/domain/healthcare/authorization.domain.md +26 -0
- package/assets/guardrails/domain/healthcare/clinical-terms.md +20 -0
- package/assets/guardrails/domain/healthcare/coding-standards.domain.md +26 -0
- package/assets/guardrails/domain/healthcare/consent.md +19 -0
- package/assets/guardrails/domain/healthcare/cqrs.domain.md +12 -0
- package/assets/guardrails/domain/healthcare/data-classification.domain.md +28 -0
- package/assets/guardrails/domain/healthcare/dependency-policy.domain.md +11 -0
- package/assets/guardrails/domain/healthcare/dicom.md +15 -0
- package/assets/guardrails/domain/healthcare/domain-constitution.md +119 -0
- package/assets/guardrails/domain/healthcare/domain-driven-design.domain.md +28 -0
- package/assets/guardrails/domain/healthcare/eventing.domain.md +18 -0
- package/assets/guardrails/domain/healthcare/fhir.md +36 -0
- package/assets/guardrails/domain/healthcare/folder-structure.domain.md +25 -0
- package/assets/guardrails/domain/healthcare/hipaa.md +34 -0
- package/assets/guardrails/domain/healthcare/hl7.md +15 -0
- package/assets/guardrails/domain/healthcare/ihe-conformance.md +31 -0
- package/assets/guardrails/domain/healthcare/integration-terms.md +21 -0
- package/assets/guardrails/domain/healthcare/persistence.domain.md +20 -0
- package/assets/guardrails/domain/healthcare/terminology.md +30 -0
- package/assets/guardrails/domain/healthcare/testing.domain.md +15 -0
- package/assets/guardrails/domain/healthcare/ux/accessibility-standards.md +36 -0
- package/assets/guardrails/domain/healthcare/ux/content-design.md +32 -0
- package/assets/guardrails/domain/healthcare/ux/interaction-states.md +44 -0
- package/assets/guardrails/domain/healthcare/ux/journey-mapping.md +40 -0
- package/assets/guardrails/domain/healthcare/vertical-slices.domain.md +18 -0
- package/assets/guardrails/domain/healthcare/zero-trust.domain.md +12 -0
- package/assets/guardrails/domain/pharma/ai-rules.domain.md +32 -0
- package/assets/guardrails/domain/pharma/api-versioning.domain.md +20 -0
- package/assets/guardrails/domain/pharma/atc.md +22 -0
- package/assets/guardrails/domain/pharma/auditing.domain.md +90 -0
- package/assets/guardrails/domain/pharma/authorization.domain.md +25 -0
- package/assets/guardrails/domain/pharma/coding-standards.domain.md +28 -0
- package/assets/guardrails/domain/pharma/cqrs.domain.md +16 -0
- package/assets/guardrails/domain/pharma/data-classification.domain.md +24 -0
- package/assets/guardrails/domain/pharma/data-provenance.md +21 -0
- package/assets/guardrails/domain/pharma/dependency-policy.domain.md +11 -0
- package/assets/guardrails/domain/pharma/domain-constitution.md +113 -0
- package/assets/guardrails/domain/pharma/domain-driven-design.domain.md +36 -0
- package/assets/guardrails/domain/pharma/domain-requirements.sample.md +103 -0
- package/assets/guardrails/domain/pharma/eventing.domain.md +18 -0
- package/assets/guardrails/domain/pharma/fhir.md +31 -0
- package/assets/guardrails/domain/pharma/folder-structure.domain.md +26 -0
- package/assets/guardrails/domain/pharma/hl7.md +17 -0
- package/assets/guardrails/domain/pharma/idmp.md +26 -0
- package/assets/guardrails/domain/pharma/integration-terms.md +21 -0
- package/assets/guardrails/domain/pharma/ndc.md +28 -0
- package/assets/guardrails/domain/pharma/persistence.domain.md +23 -0
- package/assets/guardrails/domain/pharma/pharma-terms.md +27 -0
- package/assets/guardrails/domain/pharma/product-lifecycle.domain.md +30 -0
- package/assets/guardrails/domain/pharma/regulatory-compliance.md +19 -0
- package/assets/guardrails/domain/pharma/terminology.md +32 -0
- package/assets/guardrails/domain/pharma/testing.domain.md +19 -0
- package/assets/guardrails/domain/pharma/vertical-slices.domain.md +17 -0
- package/assets/guardrails/domain/pharma/zero-trust.domain.md +12 -0
- package/assets/guardrails/projects/dotnet-default-project/domain/domain-requirements.md +58 -0
- package/assets/guardrails/projects/dotnet-default-project/domain-rule-bindings.md +113 -0
- package/assets/guardrails/projects/dotnet-default-project/frontend/ui-requirements.md +43 -0
- package/assets/guardrails/projects/dotnet-default-project/service/technical-requirements.md +40 -0
- package/assets/guardrails/projects/dotnet-default-project/tech-rule-bindings.md +263 -0
- package/assets/guardrails/requirement-spec.txt +27 -0
- package/assets/guardrails/technology/dotnet-webapi/PACKAGING.md +184 -0
- package/assets/guardrails/technology/dotnet-webapi/api-versioning.md +44 -0
- package/assets/guardrails/technology/dotnet-webapi/auditing.md +96 -0
- package/assets/guardrails/technology/dotnet-webapi/authorization-matrix.md +14 -0
- package/assets/guardrails/technology/dotnet-webapi/authorization.md +39 -0
- package/assets/guardrails/technology/dotnet-webapi/clean-architecture.md +54 -0
- package/assets/guardrails/technology/dotnet-webapi/coding-standards.md +87 -0
- package/assets/guardrails/technology/dotnet-webapi/cqrs.md +63 -0
- package/assets/guardrails/technology/dotnet-webapi/data-classification.md +38 -0
- package/assets/guardrails/technology/dotnet-webapi/dependency-policy.md +40 -0
- package/assets/guardrails/technology/dotnet-webapi/domain-driven-design.md +91 -0
- package/assets/guardrails/technology/dotnet-webapi/eventing.md +57 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/api-versioning.md +75 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/auditing.md +84 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/authorization.md +68 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/clean-architecture.md +37 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/cqrs.md +97 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/domain-driven-design.md +141 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/eventing.md +46 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/persistence.md +207 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/vertical-slices.md +151 -0
- package/assets/guardrails/technology/dotnet-webapi/examples/zero-trust.md +48 -0
- package/assets/guardrails/technology/dotnet-webapi/folder-structure.md +61 -0
- package/assets/guardrails/technology/dotnet-webapi/modular-monolith.md +47 -0
- package/assets/guardrails/technology/dotnet-webapi/persistence.md +82 -0
- package/assets/guardrails/technology/dotnet-webapi/technology-constitution.md +162 -0
- package/assets/guardrails/technology/dotnet-webapi/testing.md +55 -0
- package/assets/guardrails/technology/dotnet-webapi/vertical-slices.md +70 -0
- package/assets/guardrails/technology/dotnet-webapi/zero-trust.md +48 -0
- package/assets/guardrails/technology/java-spring/README.md +62 -0
- package/assets/guardrails/technology/nextjs/accessibility-implementation.md +56 -0
- package/assets/guardrails/technology/nextjs/api-client.md +56 -0
- package/assets/guardrails/technology/nextjs/api-integration.dotnet-webapi.md +174 -0
- package/assets/guardrails/technology/nextjs/api-integration.java-spring.md +203 -0
- package/assets/guardrails/technology/nextjs/auth-session.md +68 -0
- package/assets/guardrails/technology/nextjs/client-security.md +69 -0
- package/assets/guardrails/technology/nextjs/component-architecture.md +60 -0
- package/assets/guardrails/technology/nextjs/component-conventions.md +58 -0
- package/assets/guardrails/technology/nextjs/design-system.md +60 -0
- package/assets/guardrails/technology/nextjs/error-handling.md +72 -0
- package/assets/guardrails/technology/nextjs/forms-and-validation.md +75 -0
- package/assets/guardrails/technology/nextjs/frontend-testing.md +94 -0
- package/assets/guardrails/technology/nextjs/i18n.md +57 -0
- package/assets/guardrails/technology/nextjs/observability.md +67 -0
- package/assets/guardrails/technology/nextjs/performance-budgets.md +66 -0
- package/assets/guardrails/technology/nextjs/routing-navigation.md +52 -0
- package/assets/guardrails/technology/nextjs/state-management.md +70 -0
- package/assets/guardrails/technology/nextjs/ui-constitution.md +166 -0
- package/package.json +1 -1
- package/assets/docs/pharma-constitution.my-ref.md +0 -86
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# aw-xref.sh — thin wrapper: delegate to the single python core.
|
|
3
|
+
#
|
|
4
|
+
# Cross-reference (OKF dependency) substrate. Logic lives ONCE in
|
|
5
|
+
# ../python/aw-xref.py; this wrapper exists only so the orchestrator's
|
|
6
|
+
# execute_script (sh:/ps1: dispatch) can invoke it. Args pass through.
|
|
7
|
+
#
|
|
8
|
+
# aw-xref.sh record --using X --design <design.md> [--origin ...] [--date YYYY-MM-DD] [--root DIR]
|
|
9
|
+
# aw-xref.sh list --aggregate Y [--root DIR] [--bundle knowledge]
|
|
10
|
+
# aw-xref.sh concept-upsert --aggregate X [--source-files a,b,c] [--description D] [--origin ...] [--root DIR]
|
|
11
|
+
# aw-xref.sh concept-overview --aggregate X (--text PROSE | --text-file F) [--root DIR]
|
|
12
|
+
# aw-xref.sh concept-paths --aggregate X | --feature-dir <dir> [--root DIR] [--bundle knowledge]
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
15
|
+
exec python3 "$DIR/../python/aw-xref.py" "$@"
|
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Common functions and variables for all scripts
|
|
3
|
+
|
|
4
|
+
# Find repository root by searching upward for .appweaver directory
|
|
5
|
+
# This is the primary marker for aw-spec projects
|
|
6
|
+
find_specify_root() {
|
|
7
|
+
local dir="${1:-$(pwd)}"
|
|
8
|
+
# Normalize to absolute path to prevent infinite loop with relative paths
|
|
9
|
+
# Use -- to handle paths starting with - (e.g., -P, -L)
|
|
10
|
+
dir="$(cd -- "$dir" 2>/dev/null && pwd)" || return 1
|
|
11
|
+
local prev_dir=""
|
|
12
|
+
while true; do
|
|
13
|
+
if [ -d "$dir/.appweaver" ]; then
|
|
14
|
+
echo "$dir"
|
|
15
|
+
return 0
|
|
16
|
+
fi
|
|
17
|
+
# Stop if we've reached filesystem root or dirname stops changing
|
|
18
|
+
if [ "$dir" = "/" ] || [ "$dir" = "$prev_dir" ]; then
|
|
19
|
+
break
|
|
20
|
+
fi
|
|
21
|
+
prev_dir="$dir"
|
|
22
|
+
dir="$(dirname "$dir")"
|
|
23
|
+
done
|
|
24
|
+
return 1
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Get repository root, prioritizing .appweaver directory
|
|
28
|
+
# This prevents using a parent repository when aw-spec is initialized in a subdirectory
|
|
29
|
+
get_repo_root() {
|
|
30
|
+
# First, look for .appweaver directory (aw-spec's own marker)
|
|
31
|
+
local specify_root
|
|
32
|
+
if specify_root=$(find_specify_root); then
|
|
33
|
+
echo "$specify_root"
|
|
34
|
+
return
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
# Final fallback to script location
|
|
38
|
+
local script_dir="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
39
|
+
(cd "$script_dir/../../.." && pwd)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# FEATURE RESOLUTION IS SINGLE-CHANNEL: `.appweaver/feature.json`, written by
|
|
43
|
+
# select-feature (and adopt), read back by read_feature_json_feature_directory below.
|
|
44
|
+
# There is deliberately NO environment override. The retired get_feature_paths() +
|
|
45
|
+
# get_current_branch() pair honoured SPECIFY_FEATURE_DIRECTORY / SPECIFY_FEATURE, but
|
|
46
|
+
# they were dead code (zero callers) emitting the retired plan.md/tasks.md/research.md
|
|
47
|
+
# document set, and PowerShell never implemented either variable — so the "override"
|
|
48
|
+
# existed on one shell only and nothing in the pipeline could observe it. A second,
|
|
49
|
+
# invisible channel would also let one step diverge from the feature every OTHER step
|
|
50
|
+
# reads. Callers that need to act on a specific feature take an explicit `--feature-dir`
|
|
51
|
+
# argument, which wins over feature.json at the call site.
|
|
52
|
+
|
|
53
|
+
# Safely read .appweaver/feature.json's "feature_directory" value.
|
|
54
|
+
# Prints the raw value (possibly relative) to stdout, or empty string if the file
|
|
55
|
+
# is missing, unparseable, or does not contain the key. Always returns 0 so callers
|
|
56
|
+
# under `set -e` cannot be aborted by parser failure.
|
|
57
|
+
# Parser order: jq -> python3 -> grep/sed.
|
|
58
|
+
read_feature_json_feature_directory() {
|
|
59
|
+
local repo_root="$1"
|
|
60
|
+
local fj="$repo_root/.appweaver/feature.json"
|
|
61
|
+
[[ -f "$fj" ]] || { printf '%s' ''; return 0; }
|
|
62
|
+
|
|
63
|
+
local _fd=''
|
|
64
|
+
if command -v jq >/dev/null 2>&1; then
|
|
65
|
+
if ! _fd=$(jq -r '.feature_directory // empty' "$fj" 2>/dev/null); then
|
|
66
|
+
_fd=''
|
|
67
|
+
fi
|
|
68
|
+
elif command -v python3 >/dev/null 2>&1; then
|
|
69
|
+
# Use Python so pretty-printed/multi-line JSON still parses correctly.
|
|
70
|
+
if ! _fd=$(python3 -c "import json,sys; d=json.load(open(sys.argv[1])); v=d.get('feature_directory'); print(v if v else '')" "$fj" 2>/dev/null); then
|
|
71
|
+
_fd=''
|
|
72
|
+
fi
|
|
73
|
+
else
|
|
74
|
+
# Last-resort single-line grep/sed fallback. The `|| true` guards against
|
|
75
|
+
# grep returning 1 (no match) aborting under `set -e` / `pipefail`.
|
|
76
|
+
_fd=$( { grep -E '"feature_directory"[[:space:]]*:' "$fj" 2>/dev/null || true; } \
|
|
77
|
+
| head -n 1 \
|
|
78
|
+
| sed -E 's/^[^:]*:[[:space:]]*"([^"]*)".*$/\1/' )
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
printf '%s' "$_fd"
|
|
82
|
+
return 0
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
# Persist a feature_directory value to .appweaver/feature.json.
|
|
86
|
+
# Writes only when the file is missing or the value differs from what's stored.
|
|
87
|
+
# Accepts the raw (possibly relative) path — callers should pass the original
|
|
88
|
+
# user-supplied value, not the normalized absolute path.
|
|
89
|
+
_persist_feature_json() {
|
|
90
|
+
local repo_root="$1"
|
|
91
|
+
local feature_dir_value="$2"
|
|
92
|
+
local fj="$repo_root/.appweaver/feature.json"
|
|
93
|
+
|
|
94
|
+
# Strip repo_root prefix if the value is absolute and under repo_root
|
|
95
|
+
if [[ "$feature_dir_value" == "$repo_root/"* ]]; then
|
|
96
|
+
feature_dir_value="${feature_dir_value#"$repo_root/"}"
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
# Read current value (if any) and skip write when unchanged
|
|
100
|
+
local current_val
|
|
101
|
+
current_val=$(read_feature_json_feature_directory "$repo_root")
|
|
102
|
+
if [[ "$current_val" == "$feature_dir_value" ]]; then
|
|
103
|
+
return 0
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
# Ensure .appweaver/ directory exists
|
|
107
|
+
mkdir -p "$repo_root/.appweaver"
|
|
108
|
+
|
|
109
|
+
# Write feature.json — prefer jq for safe JSON, fall back to printf
|
|
110
|
+
if command -v jq >/dev/null 2>&1; then
|
|
111
|
+
jq -cn --arg fd "$feature_dir_value" '{feature_directory:$fd}' > "$fj"
|
|
112
|
+
else
|
|
113
|
+
printf '{"feature_directory":"%s"}\n' "$(json_escape "$feature_dir_value")" > "$fj"
|
|
114
|
+
fi
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# Check if jq is available for safe JSON construction
|
|
118
|
+
has_jq() {
|
|
119
|
+
command -v jq >/dev/null 2>&1
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
get_invoke_separator() {
|
|
123
|
+
local repo_root="${1:-$(get_repo_root)}"
|
|
124
|
+
if [[ "${_SPECIFY_INVOKE_SEPARATOR_CACHE_REPO_ROOT:-}" == "$repo_root" && -n "${_SPECIFY_INVOKE_SEPARATOR_CACHE_VALUE:-}" ]]; then
|
|
125
|
+
printf '%s\n' "$_SPECIFY_INVOKE_SEPARATOR_CACHE_VALUE"
|
|
126
|
+
return 0
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
local integration_json="$repo_root/.appweaver/integration.json"
|
|
130
|
+
local separator="."
|
|
131
|
+
local parsed_with_jq=0
|
|
132
|
+
|
|
133
|
+
if [[ -f "$integration_json" ]]; then
|
|
134
|
+
if command -v jq >/dev/null 2>&1; then
|
|
135
|
+
local jq_separator
|
|
136
|
+
if jq_separator=$(jq -r '(.default_integration // .integration // "") as $k | if $k == "" then "." else (.integration_settings[$k].invoke_separator // ".") end' "$integration_json" 2>/dev/null); then
|
|
137
|
+
parsed_with_jq=1
|
|
138
|
+
case "$jq_separator" in
|
|
139
|
+
"."|"-") separator="$jq_separator" ;;
|
|
140
|
+
esac
|
|
141
|
+
fi
|
|
142
|
+
fi
|
|
143
|
+
|
|
144
|
+
if [[ "$parsed_with_jq" -eq 0 ]] && command -v python3 >/dev/null 2>&1; then
|
|
145
|
+
if separator=$(python3 - "$integration_json" <<'PY' 2>/dev/null
|
|
146
|
+
import json
|
|
147
|
+
import sys
|
|
148
|
+
|
|
149
|
+
try:
|
|
150
|
+
with open(sys.argv[1], encoding="utf-8") as fh:
|
|
151
|
+
state = json.load(fh)
|
|
152
|
+
key = state.get("default_integration") or state.get("integration") or ""
|
|
153
|
+
settings = state.get("integration_settings")
|
|
154
|
+
separator = "."
|
|
155
|
+
if isinstance(key, str) and isinstance(settings, dict):
|
|
156
|
+
entry = settings.get(key)
|
|
157
|
+
if isinstance(entry, dict) and entry.get("invoke_separator") in {".", "-"}:
|
|
158
|
+
separator = entry["invoke_separator"]
|
|
159
|
+
print(separator)
|
|
160
|
+
except Exception:
|
|
161
|
+
print(".")
|
|
162
|
+
PY
|
|
163
|
+
); then
|
|
164
|
+
case "$separator" in
|
|
165
|
+
"."|"-") ;;
|
|
166
|
+
*) separator="." ;;
|
|
167
|
+
esac
|
|
168
|
+
else
|
|
169
|
+
separator="."
|
|
170
|
+
fi
|
|
171
|
+
fi
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
_SPECIFY_INVOKE_SEPARATOR_CACHE_REPO_ROOT="$repo_root"
|
|
175
|
+
_SPECIFY_INVOKE_SEPARATOR_CACHE_VALUE="$separator"
|
|
176
|
+
printf '%s\n' "$separator"
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
format_aw-spec_command() {
|
|
180
|
+
local command_name="$1"
|
|
181
|
+
local repo_root="${2:-$(get_repo_root)}"
|
|
182
|
+
local separator
|
|
183
|
+
if [[ "${_SPECIFY_INVOKE_SEPARATOR_CACHE_REPO_ROOT:-}" == "$repo_root" && -n "${_SPECIFY_INVOKE_SEPARATOR_CACHE_VALUE:-}" ]]; then
|
|
184
|
+
separator="$_SPECIFY_INVOKE_SEPARATOR_CACHE_VALUE"
|
|
185
|
+
else
|
|
186
|
+
separator=$(get_invoke_separator "$repo_root")
|
|
187
|
+
_SPECIFY_INVOKE_SEPARATOR_CACHE_REPO_ROOT="$repo_root"
|
|
188
|
+
_SPECIFY_INVOKE_SEPARATOR_CACHE_VALUE="$separator"
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
command_name="${command_name#/}"
|
|
192
|
+
command_name="${command_name#aw-spec.}"
|
|
193
|
+
command_name="${command_name#aw-spec-}"
|
|
194
|
+
command_name="${command_name//./$separator}"
|
|
195
|
+
|
|
196
|
+
printf '/aw-spec%s%s\n' "$separator" "$command_name"
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
# Escape a string for safe embedding in a JSON value (fallback when jq is unavailable).
|
|
200
|
+
# Handles backslash, double-quote, and JSON-required control character escapes (RFC 8259).
|
|
201
|
+
json_escape() {
|
|
202
|
+
local s="$1"
|
|
203
|
+
s="${s//\\/\\\\}"
|
|
204
|
+
s="${s//\"/\\\"}"
|
|
205
|
+
s="${s//$'\n'/\\n}"
|
|
206
|
+
s="${s//$'\t'/\\t}"
|
|
207
|
+
s="${s//$'\r'/\\r}"
|
|
208
|
+
s="${s//$'\b'/\\b}"
|
|
209
|
+
s="${s//$'\f'/\\f}"
|
|
210
|
+
# Escape any remaining U+0001-U+001F control characters as \uXXXX.
|
|
211
|
+
# (U+0000/NUL cannot appear in bash strings and is excluded.)
|
|
212
|
+
# LC_ALL=C ensures ${#s} counts bytes and ${s:$i:1} yields single bytes,
|
|
213
|
+
# so multi-byte UTF-8 sequences (first byte >= 0xC0) pass through intact.
|
|
214
|
+
local LC_ALL=C
|
|
215
|
+
local i char code
|
|
216
|
+
for (( i=0; i<${#s}; i++ )); do
|
|
217
|
+
char="${s:$i:1}"
|
|
218
|
+
printf -v code '%d' "'$char" 2>/dev/null || code=256
|
|
219
|
+
if (( code >= 1 && code <= 31 )); then
|
|
220
|
+
printf '\\u%04x' "$code"
|
|
221
|
+
else
|
|
222
|
+
printf '%s' "$char"
|
|
223
|
+
fi
|
|
224
|
+
done
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
check_file() { [[ -f "$1" ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
|
228
|
+
check_dir() { [[ -d "$1" && -n $(ls -A "$1" 2>/dev/null) ]] && echo " ✓ $2" || echo " ✗ $2"; }
|
|
229
|
+
|
|
230
|
+
# Resolve a template name to a file path using the priority stack:
|
|
231
|
+
# 1. .appweaver/templates/overrides/
|
|
232
|
+
# 2. .appweaver/presets/<preset-id>/templates/ (sorted by priority from .registry)
|
|
233
|
+
# 3. .appweaver/extensions/<ext-id>/templates/
|
|
234
|
+
# 4. .appweaver/templates/ (core)
|
|
235
|
+
resolve_template() {
|
|
236
|
+
local template_name="$1"
|
|
237
|
+
local repo_root="$2"
|
|
238
|
+
local base="$repo_root/.appweaver/templates"
|
|
239
|
+
|
|
240
|
+
# Priority 1: Project overrides
|
|
241
|
+
local override="$base/overrides/${template_name}.md"
|
|
242
|
+
[ -f "$override" ] && echo "$override" && return 0
|
|
243
|
+
|
|
244
|
+
# Priority 2: Installed presets (sorted by priority from .registry)
|
|
245
|
+
local presets_dir="$repo_root/.appweaver/presets"
|
|
246
|
+
if [ -d "$presets_dir" ]; then
|
|
247
|
+
local registry_file="$presets_dir/.registry"
|
|
248
|
+
if [ -f "$registry_file" ] && command -v python3 >/dev/null 2>&1; then
|
|
249
|
+
# Read preset IDs sorted by priority (lower number = higher precedence).
|
|
250
|
+
# The python3 call is wrapped in an if-condition so that set -e does not
|
|
251
|
+
# abort the function when python3 exits non-zero (e.g. invalid JSON).
|
|
252
|
+
local sorted_presets=""
|
|
253
|
+
if sorted_presets=$(AW_SPEC_REGISTRY="$registry_file" python3 -c "
|
|
254
|
+
import json, sys, os
|
|
255
|
+
try:
|
|
256
|
+
with open(os.environ['AW_SPEC_REGISTRY']) as f:
|
|
257
|
+
data = json.load(f)
|
|
258
|
+
presets = data.get('presets', {})
|
|
259
|
+
for pid, meta in sorted(presets.items(), key=lambda x: x[1].get('priority', 10) if isinstance(x[1], dict) else 10):
|
|
260
|
+
if isinstance(meta, dict) and meta.get('enabled', True) is not False:
|
|
261
|
+
print(pid)
|
|
262
|
+
except Exception:
|
|
263
|
+
sys.exit(1)
|
|
264
|
+
" 2>/dev/null); then
|
|
265
|
+
if [ -n "$sorted_presets" ]; then
|
|
266
|
+
# python3 succeeded and returned preset IDs — search in priority order
|
|
267
|
+
while IFS= read -r preset_id; do
|
|
268
|
+
local candidate="$presets_dir/$preset_id/templates/${template_name}.md"
|
|
269
|
+
[ -f "$candidate" ] && echo "$candidate" && return 0
|
|
270
|
+
done <<< "$sorted_presets"
|
|
271
|
+
fi
|
|
272
|
+
# python3 succeeded but registry has no presets — nothing to search
|
|
273
|
+
else
|
|
274
|
+
# python3 failed (missing, or registry parse error) — fall back to unordered directory scan
|
|
275
|
+
for preset in "$presets_dir"/*/; do
|
|
276
|
+
[ -d "$preset" ] || continue
|
|
277
|
+
local candidate="$preset/templates/${template_name}.md"
|
|
278
|
+
[ -f "$candidate" ] && echo "$candidate" && return 0
|
|
279
|
+
done
|
|
280
|
+
fi
|
|
281
|
+
else
|
|
282
|
+
# Fallback: alphabetical directory order (no python3 available)
|
|
283
|
+
for preset in "$presets_dir"/*/; do
|
|
284
|
+
[ -d "$preset" ] || continue
|
|
285
|
+
local candidate="$preset/templates/${template_name}.md"
|
|
286
|
+
[ -f "$candidate" ] && echo "$candidate" && return 0
|
|
287
|
+
done
|
|
288
|
+
fi
|
|
289
|
+
fi
|
|
290
|
+
|
|
291
|
+
# Priority 3: Extension-provided templates
|
|
292
|
+
local ext_dir="$repo_root/.appweaver/extensions"
|
|
293
|
+
if [ -d "$ext_dir" ]; then
|
|
294
|
+
for ext in "$ext_dir"/*/; do
|
|
295
|
+
[ -d "$ext" ] || continue
|
|
296
|
+
# Skip hidden directories (e.g. .backup, .cache)
|
|
297
|
+
case "$(basename "$ext")" in .*) continue;; esac
|
|
298
|
+
local candidate="$ext/templates/${template_name}.md"
|
|
299
|
+
[ -f "$candidate" ] && echo "$candidate" && return 0
|
|
300
|
+
done
|
|
301
|
+
fi
|
|
302
|
+
|
|
303
|
+
# Priority 4: Core templates
|
|
304
|
+
local core="$base/${template_name}.md"
|
|
305
|
+
[ -f "$core" ] && echo "$core" && return 0
|
|
306
|
+
|
|
307
|
+
# Template not found in any location.
|
|
308
|
+
# Return 1 so callers can distinguish "not found" from "found".
|
|
309
|
+
# Callers running under set -e should use: TEMPLATE=$(resolve_template ...) || true
|
|
310
|
+
return 1
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
# Resolve a template name to composed content using composition strategies.
|
|
314
|
+
# Reads strategy metadata from preset manifests and composes content
|
|
315
|
+
# from multiple layers using prepend, append, or wrap strategies.
|
|
316
|
+
#
|
|
317
|
+
# Usage: CONTENT=$(resolve_template_content "template-name" "$REPO_ROOT")
|
|
318
|
+
# Returns composed content string on stdout; exit code 1 if not found.
|
|
319
|
+
resolve_template_content() {
|
|
320
|
+
local template_name="$1"
|
|
321
|
+
local repo_root="$2"
|
|
322
|
+
local base="$repo_root/.appweaver/templates"
|
|
323
|
+
|
|
324
|
+
# Collect all layers (highest priority first)
|
|
325
|
+
local -a layer_paths=()
|
|
326
|
+
local -a layer_strategies=()
|
|
327
|
+
|
|
328
|
+
# Priority 1: Project overrides (always "replace")
|
|
329
|
+
local override="$base/overrides/${template_name}.md"
|
|
330
|
+
if [ -f "$override" ]; then
|
|
331
|
+
layer_paths+=("$override")
|
|
332
|
+
layer_strategies+=("replace")
|
|
333
|
+
fi
|
|
334
|
+
|
|
335
|
+
# Priority 2: Installed presets (sorted by priority from .registry)
|
|
336
|
+
local presets_dir="$repo_root/.appweaver/presets"
|
|
337
|
+
if [ -d "$presets_dir" ]; then
|
|
338
|
+
local registry_file="$presets_dir/.registry"
|
|
339
|
+
local sorted_presets=""
|
|
340
|
+
if [ -f "$registry_file" ] && command -v python3 >/dev/null 2>&1; then
|
|
341
|
+
if sorted_presets=$(AW_SPEC_REGISTRY="$registry_file" python3 -c "
|
|
342
|
+
import json, sys, os
|
|
343
|
+
try:
|
|
344
|
+
with open(os.environ['AW_SPEC_REGISTRY']) as f:
|
|
345
|
+
data = json.load(f)
|
|
346
|
+
presets = data.get('presets', {})
|
|
347
|
+
for pid, meta in sorted(presets.items(), key=lambda x: x[1].get('priority', 10) if isinstance(x[1], dict) else 10):
|
|
348
|
+
if isinstance(meta, dict) and meta.get('enabled', True) is not False:
|
|
349
|
+
print(pid)
|
|
350
|
+
except Exception:
|
|
351
|
+
sys.exit(1)
|
|
352
|
+
" 2>/dev/null); then
|
|
353
|
+
if [ -n "$sorted_presets" ]; then
|
|
354
|
+
local yaml_warned=false
|
|
355
|
+
while IFS= read -r preset_id; do
|
|
356
|
+
# Read strategy and file path from preset manifest
|
|
357
|
+
local strategy="replace"
|
|
358
|
+
local manifest_file=""
|
|
359
|
+
local manifest="$presets_dir/$preset_id/preset.yml"
|
|
360
|
+
if [ -f "$manifest" ] && command -v python3 >/dev/null 2>&1; then
|
|
361
|
+
# Requires PyYAML; falls back to replace/convention if unavailable
|
|
362
|
+
local result
|
|
363
|
+
local py_stderr
|
|
364
|
+
py_stderr=$(mktemp)
|
|
365
|
+
result=$(AW_SPEC_MANIFEST="$manifest" AW_SPEC_TMPL="$template_name" python3 -c "
|
|
366
|
+
import sys, os
|
|
367
|
+
try:
|
|
368
|
+
import yaml
|
|
369
|
+
except ImportError:
|
|
370
|
+
print('yaml_missing', file=sys.stderr)
|
|
371
|
+
print('replace\t')
|
|
372
|
+
sys.exit(0)
|
|
373
|
+
try:
|
|
374
|
+
with open(os.environ['AW_SPEC_MANIFEST']) as f:
|
|
375
|
+
data = yaml.safe_load(f)
|
|
376
|
+
for t in data.get('provides', {}).get('templates', []):
|
|
377
|
+
if t.get('name') == os.environ['AW_SPEC_TMPL'] and t.get('type', 'template') == 'template':
|
|
378
|
+
print(t.get('strategy', 'replace') + '\t' + t.get('file', ''))
|
|
379
|
+
sys.exit(0)
|
|
380
|
+
print('replace\t')
|
|
381
|
+
except Exception:
|
|
382
|
+
print('replace\t')
|
|
383
|
+
" 2>"$py_stderr")
|
|
384
|
+
local parse_status=$?
|
|
385
|
+
if [ $parse_status -eq 0 ] && [ -n "$result" ]; then
|
|
386
|
+
IFS=$'\t' read -r strategy manifest_file <<< "$result"
|
|
387
|
+
strategy=$(printf '%s' "$strategy" | tr '[:upper:]' '[:lower:]')
|
|
388
|
+
fi
|
|
389
|
+
if [ "$yaml_warned" = false ] && grep -q 'yaml_missing' "$py_stderr" 2>/dev/null; then
|
|
390
|
+
echo "Warning: PyYAML not available; composition strategies may be ignored" >&2
|
|
391
|
+
yaml_warned=true
|
|
392
|
+
fi
|
|
393
|
+
rm -f "$py_stderr"
|
|
394
|
+
fi
|
|
395
|
+
# Try manifest file path first, then convention path
|
|
396
|
+
local candidate=""
|
|
397
|
+
if [ -n "$manifest_file" ]; then
|
|
398
|
+
# Reject absolute paths and parent traversal
|
|
399
|
+
case "$manifest_file" in
|
|
400
|
+
/*|*../*|../*) manifest_file="" ;;
|
|
401
|
+
esac
|
|
402
|
+
fi
|
|
403
|
+
if [ -n "$manifest_file" ]; then
|
|
404
|
+
local mf="$presets_dir/$preset_id/$manifest_file"
|
|
405
|
+
[ -f "$mf" ] && candidate="$mf"
|
|
406
|
+
fi
|
|
407
|
+
if [ -z "$candidate" ]; then
|
|
408
|
+
local cf="$presets_dir/$preset_id/templates/${template_name}.md"
|
|
409
|
+
[ -f "$cf" ] && candidate="$cf"
|
|
410
|
+
fi
|
|
411
|
+
if [ -n "$candidate" ]; then
|
|
412
|
+
layer_paths+=("$candidate")
|
|
413
|
+
layer_strategies+=("$strategy")
|
|
414
|
+
fi
|
|
415
|
+
done <<< "$sorted_presets"
|
|
416
|
+
fi
|
|
417
|
+
else
|
|
418
|
+
# python3 failed — fall back to unordered directory scan (replace only)
|
|
419
|
+
for preset in "$presets_dir"/*/; do
|
|
420
|
+
[ -d "$preset" ] || continue
|
|
421
|
+
local candidate="$preset/templates/${template_name}.md"
|
|
422
|
+
if [ -f "$candidate" ]; then
|
|
423
|
+
layer_paths+=("$candidate")
|
|
424
|
+
layer_strategies+=("replace")
|
|
425
|
+
fi
|
|
426
|
+
done
|
|
427
|
+
fi
|
|
428
|
+
else
|
|
429
|
+
# No python3 or registry — fall back to unordered directory scan (replace only)
|
|
430
|
+
for preset in "$presets_dir"/*/; do
|
|
431
|
+
[ -d "$preset" ] || continue
|
|
432
|
+
local candidate="$preset/templates/${template_name}.md"
|
|
433
|
+
if [ -f "$candidate" ]; then
|
|
434
|
+
layer_paths+=("$candidate")
|
|
435
|
+
layer_strategies+=("replace")
|
|
436
|
+
fi
|
|
437
|
+
done
|
|
438
|
+
fi
|
|
439
|
+
fi
|
|
440
|
+
|
|
441
|
+
# Priority 3: Extension-provided templates (always "replace")
|
|
442
|
+
local ext_dir="$repo_root/.appweaver/extensions"
|
|
443
|
+
if [ -d "$ext_dir" ]; then
|
|
444
|
+
for ext in "$ext_dir"/*/; do
|
|
445
|
+
[ -d "$ext" ] || continue
|
|
446
|
+
case "$(basename "$ext")" in .*) continue;; esac
|
|
447
|
+
local candidate="$ext/templates/${template_name}.md"
|
|
448
|
+
if [ -f "$candidate" ]; then
|
|
449
|
+
layer_paths+=("$candidate")
|
|
450
|
+
layer_strategies+=("replace")
|
|
451
|
+
fi
|
|
452
|
+
done
|
|
453
|
+
fi
|
|
454
|
+
|
|
455
|
+
# Priority 4: Core templates (always "replace")
|
|
456
|
+
local core="$base/${template_name}.md"
|
|
457
|
+
if [ -f "$core" ]; then
|
|
458
|
+
layer_paths+=("$core")
|
|
459
|
+
layer_strategies+=("replace")
|
|
460
|
+
fi
|
|
461
|
+
|
|
462
|
+
local count=${#layer_paths[@]}
|
|
463
|
+
[ "$count" -eq 0 ] && return 1
|
|
464
|
+
|
|
465
|
+
# Check if any layer uses a non-replace strategy
|
|
466
|
+
local has_composition=false
|
|
467
|
+
for s in "${layer_strategies[@]}"; do
|
|
468
|
+
[ "$s" != "replace" ] && has_composition=true && break
|
|
469
|
+
done
|
|
470
|
+
|
|
471
|
+
# If the top (highest-priority) layer is replace, it wins entirely —
|
|
472
|
+
# lower layers are irrelevant regardless of their strategies.
|
|
473
|
+
if [ "${layer_strategies[0]}" = "replace" ]; then
|
|
474
|
+
cat "${layer_paths[0]}"
|
|
475
|
+
return 0
|
|
476
|
+
fi
|
|
477
|
+
|
|
478
|
+
if [ "$has_composition" = false ]; then
|
|
479
|
+
cat "${layer_paths[0]}"
|
|
480
|
+
return 0
|
|
481
|
+
fi
|
|
482
|
+
|
|
483
|
+
# Find the effective base: scan from highest priority (index 0) downward
|
|
484
|
+
# to find the nearest replace layer. Only compose layers above that base.
|
|
485
|
+
local base_idx=-1
|
|
486
|
+
local i
|
|
487
|
+
for (( i=0; i<count; i++ )); do
|
|
488
|
+
if [ "${layer_strategies[$i]}" = "replace" ]; then
|
|
489
|
+
base_idx=$i
|
|
490
|
+
break
|
|
491
|
+
fi
|
|
492
|
+
done
|
|
493
|
+
|
|
494
|
+
if [ $base_idx -lt 0 ]; then
|
|
495
|
+
return 1 # no base layer found
|
|
496
|
+
fi
|
|
497
|
+
|
|
498
|
+
# Read the base content; compose layers above the base (higher priority)
|
|
499
|
+
local content
|
|
500
|
+
content=$(cat "${layer_paths[$base_idx]}"; printf x)
|
|
501
|
+
content="${content%x}"
|
|
502
|
+
|
|
503
|
+
for (( i=base_idx-1; i>=0; i-- )); do
|
|
504
|
+
local path="${layer_paths[$i]}"
|
|
505
|
+
local strat="${layer_strategies[$i]}"
|
|
506
|
+
local layer_content
|
|
507
|
+
# Preserve trailing newlines
|
|
508
|
+
layer_content=$(cat "$path"; printf x)
|
|
509
|
+
layer_content="${layer_content%x}"
|
|
510
|
+
|
|
511
|
+
case "$strat" in
|
|
512
|
+
replace) content="$layer_content" ;;
|
|
513
|
+
prepend) content="$(printf '%s\n\n%s' "$layer_content" "$content")" ;;
|
|
514
|
+
append) content="$(printf '%s\n\n%s' "$content" "$layer_content")" ;;
|
|
515
|
+
wrap)
|
|
516
|
+
case "$layer_content" in
|
|
517
|
+
*'{CORE_TEMPLATE}'*) ;;
|
|
518
|
+
*) echo "Error: wrap strategy missing {CORE_TEMPLATE} placeholder" >&2; return 1 ;;
|
|
519
|
+
esac
|
|
520
|
+
while [[ "$layer_content" == *'{CORE_TEMPLATE}'* ]]; do
|
|
521
|
+
local before="${layer_content%%\{CORE_TEMPLATE\}*}"
|
|
522
|
+
local after="${layer_content#*\{CORE_TEMPLATE\}}"
|
|
523
|
+
layer_content="${before}${content}${after}"
|
|
524
|
+
done
|
|
525
|
+
content="$layer_content"
|
|
526
|
+
;;
|
|
527
|
+
*) echo "Error: unknown strategy '$strat'" >&2; return 1 ;;
|
|
528
|
+
esac
|
|
529
|
+
done
|
|
530
|
+
|
|
531
|
+
printf '%s' "$content"
|
|
532
|
+
return 0
|
|
533
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# aw-inventory.ps1 — thin wrapper: delegate to the single python core.
|
|
2
|
+
#
|
|
3
|
+
# The brownfield inventory logic lives ONCE in ../python/aw-inventory.py (a
|
|
4
|
+
# faithful cross-platform port of Reference/okf-inventory.sh + aggregate-root
|
|
5
|
+
# signals). This wrapper exists only so the orchestrator's execute_script,
|
|
6
|
+
# which dispatches sh:/ps1: variants, can invoke it. All args (subcommand +
|
|
7
|
+
# options) pass straight through.
|
|
8
|
+
#
|
|
9
|
+
# aw-inventory.ps1 scan [--root <dir>] [--out <path>] [--bundle <dir>] [--config <yml>]
|
|
10
|
+
# [--service-root <p>] [--ui-root <p>] (the declared lane paths:
|
|
11
|
+
# backend signals come from outside the UI subtree, frontend
|
|
12
|
+
# signals from inside it, so neither carries the other's files)
|
|
13
|
+
# aw-inventory.ps1 validate --bundle <dir> [--repo-root <dir>]
|
|
14
|
+
$ErrorActionPreference = "Stop"
|
|
15
|
+
$core = Join-Path $PSScriptRoot "..\python\aw-inventory.py"
|
|
16
|
+
& python3 $core @args
|
|
17
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# aw-parity.ps1 — thin wrapper: delegate to the single python core.
|
|
2
|
+
#
|
|
3
|
+
# Brownfield PARITY runner (the cutover gate). Logic lives ONCE in
|
|
4
|
+
# ../python/aw-parity.py; this wrapper exists only so the orchestrator's
|
|
5
|
+
# execute_script (sh:/ps1: dispatch) can invoke it. All args pass through.
|
|
6
|
+
#
|
|
7
|
+
# aw-parity.ps1 scaffold --feature-dir <dir>
|
|
8
|
+
# aw-parity.ps1 check --manifest <path>
|
|
9
|
+
# aw-parity.ps1 run --manifest <path> [--out <report.md>]
|
|
10
|
+
$ErrorActionPreference = "Stop"
|
|
11
|
+
$core = Join-Path $PSScriptRoot "..\python\aw-parity.py"
|
|
12
|
+
& python3 $core @args
|
|
13
|
+
exit $LASTEXITCODE
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# aw-rev-constitution.ps1 — thin wrapper: delegate to the single python core.
|
|
2
|
+
#
|
|
3
|
+
# Brownfield GOVERN-BACKWARD substrate, driven by `/appweaver onboard` (the
|
|
4
|
+
# standalone rev-constitution workflow is RETIRED). Logic lives
|
|
5
|
+
# ONCE in ..\python\aw-rev-constitution.py; this wrapper exists only so the
|
|
6
|
+
# orchestrator's execute_script (sh:/ps1: dispatch) can invoke it. Args pass through.
|
|
7
|
+
#
|
|
8
|
+
# aw-rev-constitution.ps1 list-adopted [--root <dir>] [--specs <dir>] [--bundle <dir>]
|
|
9
|
+
# aw-rev-constitution.ps1 project-setup --name <domain> [--fallback <name>] [--root <dir>]
|
|
10
|
+
$ErrorActionPreference = "Stop"
|
|
11
|
+
$core = Join-Path $PSScriptRoot "..\python\aw-rev-constitution.py"
|
|
12
|
+
& python3 $core @args
|
|
13
|
+
exit $LASTEXITCODE
|