guardian-framework 0.1.12 → 0.1.13
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/dist/cli.js
CHANGED
|
@@ -1335,7 +1335,7 @@ __export(exports_package, {
|
|
|
1335
1335
|
bin: () => bin,
|
|
1336
1336
|
author: () => author
|
|
1337
1337
|
});
|
|
1338
|
-
var name = "guardian-framework", version = "0.1.
|
|
1338
|
+
var name = "guardian-framework", version = "0.1.13", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
1339
1339
|
var init_package = __esm(() => {
|
|
1340
1340
|
exports = {
|
|
1341
1341
|
".": {
|
package/dist/exports.js
CHANGED
|
@@ -995,7 +995,7 @@ __export(exports_package, {
|
|
|
995
995
|
bin: () => bin,
|
|
996
996
|
author: () => author
|
|
997
997
|
});
|
|
998
|
-
var name = "guardian-framework", version = "0.1.
|
|
998
|
+
var name = "guardian-framework", version = "0.1.13", description = "Token-optimized agentic framework scaffolder with pi-first architecture", type = "module", main = "dist/exports.js", exports, types = "dist/exports.d.ts", bin, files, engines, scripts, publishConfig, pi, repository, homepage = "https://github.com/arman-jalili/guardian-framework#readme", bugs, dependencies, devDependencies, keywords, author = "Arman Wolkensteiner-Jalili", license = "MIT", package_default;
|
|
999
999
|
var init_package = __esm(() => {
|
|
1000
1000
|
exports = {
|
|
1001
1001
|
".": {
|
package/package.json
CHANGED
|
@@ -674,7 +674,7 @@ check_import_boundaries() {
|
|
|
674
674
|
esac
|
|
675
675
|
|
|
676
676
|
# Basic check: no cross-layer violations
|
|
677
|
-
#
|
|
677
|
+
# DDD 4-layer structure: domain/ → application/ → infrastructure/ → interfaces/
|
|
678
678
|
local violations=0
|
|
679
679
|
|
|
680
680
|
for layer_dir in app/domain app/application app/infrastructure app/api src/domain src/application src/infrastructure src/api; do
|
|
@@ -682,20 +682,49 @@ check_import_boundaries() {
|
|
|
682
682
|
while IFS= read -r file; do
|
|
683
683
|
[[ -f "$file" ]] || continue
|
|
684
684
|
if [[ "$layer_dir" == *"domain" ]]; then
|
|
685
|
-
if grep -qE "from.*infrastructure|from.*api|import.*infrastructure|import.*api" "$file" 2>/dev/null; then
|
|
685
|
+
if grep -qE "from.*infrastructure|from.*api|from.*interfaces|import.*infrastructure|import.*api|import.*interfaces" "$file" 2>/dev/null; then
|
|
686
686
|
((violations++))
|
|
687
|
-
log_fail "$check_name" "Domain layer imports from
|
|
687
|
+
log_fail "$check_name" "Domain layer imports from outer layer in $(basename "$file")"
|
|
688
688
|
fi
|
|
689
689
|
fi
|
|
690
690
|
if [[ "$layer_dir" == *"application" ]]; then
|
|
691
|
-
if grep -qE "from.*api|import.*api" "$file" 2>/dev/null; then
|
|
691
|
+
if grep -qE "from.*api|from.*interfaces|import.*api|import.*interfaces" "$file" 2>/dev/null; then
|
|
692
692
|
((violations++))
|
|
693
|
-
log_fail "$check_name" "Application layer imports from api in $(basename "$file")"
|
|
693
|
+
log_fail "$check_name" "Application layer imports from api/interfaces in $(basename "$file")"
|
|
694
694
|
fi
|
|
695
695
|
fi
|
|
696
696
|
done < <(find "$layer_dir" -name "*.py" -o -name "*.ts" -o -name "*.rs" -o -name "*.go" 2>/dev/null | head -20)
|
|
697
697
|
done
|
|
698
698
|
|
|
699
|
+
# Also check per-module 4-layer DDD structure directly under src/
|
|
700
|
+
local module_dirs
|
|
701
|
+
module_dirs=$(find src -mindepth 1 -maxdepth 1 -type d 2>/dev/null | sort)
|
|
702
|
+
for module_dir in $module_dirs; do
|
|
703
|
+
module=$(basename "$module_dir")
|
|
704
|
+
# Skip non-DDD modules (CLI, config, shared)
|
|
705
|
+
case "$module" in shared|common|config|lib|bin) continue ;; esac
|
|
706
|
+
|
|
707
|
+
# Check contracts/ anti-pattern
|
|
708
|
+
if [[ -d "${module_dir}/contracts" ]]; then
|
|
709
|
+
log_fail "$check_name" "Module '${module}' has contracts/ wrapper — should use domain/ application/ infrastructure/ interfaces/"
|
|
710
|
+
fi
|
|
711
|
+
|
|
712
|
+
# Check required DDD layers
|
|
713
|
+
local layers_present=0
|
|
714
|
+
local layers_missing=""
|
|
715
|
+
for layer in domain application infrastructure interfaces; do
|
|
716
|
+
if [[ -d "${module_dir}/${layer}" ]]; then
|
|
717
|
+
layers_present=$((layers_present + 1))
|
|
718
|
+
else
|
|
719
|
+
layers_missing="${layers_missing} ${layer}"
|
|
720
|
+
fi
|
|
721
|
+
done
|
|
722
|
+
|
|
723
|
+
if [[ "$layers_present" -gt 0 ]] && [[ "$layers_present" -lt 4 ]]; then
|
|
724
|
+
log_fail "$check_name" "Module '${module}' has incomplete DDD layers (missing:${layers_missing})"
|
|
725
|
+
fi
|
|
726
|
+
done
|
|
727
|
+
|
|
699
728
|
if [[ $violations -eq 0 ]]; then
|
|
700
729
|
log_pass "Import boundary conformance (no cross-layer violations)"
|
|
701
730
|
fi
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# check_ddd_structure.sh — Validate DDD Clean Architecture layer structure
|
|
4
|
+
#
|
|
5
|
+
# Ensures every module in src/ follows the correct 4-layer DDD structure:
|
|
6
|
+
# src/[module]/domain/
|
|
7
|
+
# src/[module]/application/
|
|
8
|
+
# src/[module]/infrastructure/
|
|
9
|
+
# src/[module]/interfaces/
|
|
10
|
+
#
|
|
11
|
+
# Also checks NO contracts/ wrapper exists (v0.1.10+ pattern) and validates
|
|
12
|
+
# dependency direction (domain must not import from outer layers).
|
|
13
|
+
#
|
|
14
|
+
# Usage:
|
|
15
|
+
# bash .pi/scripts/ci/check_ddd_structure.sh # Run all checks
|
|
16
|
+
# bash .pi/scripts/ci/check_ddd_structure.sh --module X # Single module
|
|
17
|
+
# bash .pi/scripts/ci/check_ddd_structure.sh --fix # Suggest fixes
|
|
18
|
+
#
|
|
19
|
+
# Exit: 0 = all modules conform, 1 = violations found
|
|
20
|
+
# ============================================================================
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
24
|
+
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/../../.." && pwd)"
|
|
25
|
+
SRC_DIR="${PROJECT_ROOT}/src"
|
|
26
|
+
|
|
27
|
+
RED='\033[0;31m'
|
|
28
|
+
GREEN='\033[0;32m'
|
|
29
|
+
YELLOW='\033[1;33m'
|
|
30
|
+
BLUE='\033[0;34m'
|
|
31
|
+
NC='\033[0m'
|
|
32
|
+
|
|
33
|
+
PASS_COUNT=0
|
|
34
|
+
FAIL_COUNT=0
|
|
35
|
+
ERRORS=()
|
|
36
|
+
CHECK_MODE="full"
|
|
37
|
+
SINGLE_MODULE=""
|
|
38
|
+
|
|
39
|
+
# ── Options ──
|
|
40
|
+
while [[ $# -gt 0 ]]; do
|
|
41
|
+
case $1 in
|
|
42
|
+
--module) SINGLE_MODULE="$2"; shift 2 ;;
|
|
43
|
+
--fix) CHECK_MODE="fix"; shift ;;
|
|
44
|
+
--help)
|
|
45
|
+
sed -n '3,14p' "$0" | sed 's/^#//'
|
|
46
|
+
exit 0
|
|
47
|
+
;;
|
|
48
|
+
*) shift ;;
|
|
49
|
+
esac
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
pass() { echo -e "${GREEN}✅ PASS${NC} $1"; PASS_COUNT=$((PASS_COUNT + 1)); }
|
|
53
|
+
fail() { echo -e "${RED}❌ FAIL${NC} $1"; FAIL_COUNT=$((FAIL_COUNT + 1)); ERRORS+=("$1"); }
|
|
54
|
+
warn() { echo -e "${YELLOW}⚠️ WARN${NC} $1"; }
|
|
55
|
+
|
|
56
|
+
echo "============================================"
|
|
57
|
+
echo " DDD Layer Structure Check"
|
|
58
|
+
echo "============================================"
|
|
59
|
+
echo ""
|
|
60
|
+
|
|
61
|
+
# ── Language Detection ──
|
|
62
|
+
detect_language() {
|
|
63
|
+
if [[ -f "${PROJECT_ROOT}/Cargo.toml" ]]; then echo "rust"
|
|
64
|
+
elif [[ -f "${PROJECT_ROOT}/go.mod" ]]; then echo "go"
|
|
65
|
+
elif [[ -f "${PROJECT_ROOT}/package.json" || -f "${PROJECT_ROOT}/tsconfig.json" ]]; then echo "typescript"
|
|
66
|
+
elif [[ -f "${PROJECT_ROOT}/pyproject.toml" || -f "${PROJECT_ROOT}/requirements.txt" ]]; then echo "python"
|
|
67
|
+
elif [[ -f "${PROJECT_ROOT}/pom.xml" || -f "${PROJECT_ROOT}/build.gradle" ]]; then echo "java"
|
|
68
|
+
else echo "unknown"; fi
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
PROJECT_LANG=$(detect_language)
|
|
72
|
+
echo "Language: ${BLUE}${PROJECT_LANG}${NC}"
|
|
73
|
+
echo ""
|
|
74
|
+
|
|
75
|
+
# ── File extension mapping ──
|
|
76
|
+
get_source_extensions() {
|
|
77
|
+
case "$1" in
|
|
78
|
+
rust) echo "rs" ;;
|
|
79
|
+
go) echo "go" ;;
|
|
80
|
+
typescript) echo "ts" ;;
|
|
81
|
+
python) echo "py" ;;
|
|
82
|
+
java) echo "java" ;;
|
|
83
|
+
*) echo "rs go ts py java" ;;
|
|
84
|
+
esac
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
get_import_pattern() {
|
|
88
|
+
# Returns regex patterns for import statements that violate dependency direction
|
|
89
|
+
case "$1" in
|
|
90
|
+
rust)
|
|
91
|
+
# Domain importing from infrastructure/application/interfaces is wrong
|
|
92
|
+
echo "use crate::.*infrastructure|use crate::.*application|use crate::.*interfaces"
|
|
93
|
+
;;
|
|
94
|
+
go)
|
|
95
|
+
echo '"\.\./infrastructure|"\.\./application|"\.\./interfaces'
|
|
96
|
+
;;
|
|
97
|
+
typescript)
|
|
98
|
+
echo "from.*infrastructure|from.*application|from.*interfaces"
|
|
99
|
+
;;
|
|
100
|
+
python)
|
|
101
|
+
echo "from.*infrastructure|from.*application|from.*interfaces|import.*infrastructure|import.*application|import.*interfaces"
|
|
102
|
+
;;
|
|
103
|
+
java)
|
|
104
|
+
echo "import.*infrastructure|import.*application|import.*interfaces"
|
|
105
|
+
;;
|
|
106
|
+
*)
|
|
107
|
+
echo "infrastructure|application|interfaces"
|
|
108
|
+
;;
|
|
109
|
+
esac
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
SRC_EXTS=$(get_source_extensions "$PROJECT_LANG")
|
|
113
|
+
IMPORT_PATTERN=$(get_import_pattern "$PROJECT_LANG")
|
|
114
|
+
|
|
115
|
+
# ── Discover modules ──
|
|
116
|
+
declare -a MODULES
|
|
117
|
+
if [[ -n "$SINGLE_MODULE" ]]; then
|
|
118
|
+
if [[ -d "${SRC_DIR}/${SINGLE_MODULE}" ]]; then
|
|
119
|
+
MODULES=("${SINGLE_MODULE}")
|
|
120
|
+
else
|
|
121
|
+
echo -e "${RED}Module '${SINGLE_MODULE}' not found in src/${NC}"
|
|
122
|
+
exit 1
|
|
123
|
+
fi
|
|
124
|
+
else
|
|
125
|
+
# Find top-level directories in src/ — these are modules/bounded contexts
|
|
126
|
+
while IFS= read -r -d '' dir; do
|
|
127
|
+
module=$(basename "$dir")
|
|
128
|
+
# Skip shared/ common/ config/ if they exist
|
|
129
|
+
case "$module" in
|
|
130
|
+
shared|common|config|lib) continue ;;
|
|
131
|
+
esac
|
|
132
|
+
MODULES+=("$module")
|
|
133
|
+
done < <(find "$SRC_DIR" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null | sort -z)
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
if [[ ${#MODULES[@]} -eq 0 ]]; then
|
|
137
|
+
warn "No modules found in src/ — nothing to check"
|
|
138
|
+
echo ""
|
|
139
|
+
echo "============================================"
|
|
140
|
+
echo " Summary"
|
|
141
|
+
echo "============================================"
|
|
142
|
+
echo -e " Passed: ${GREEN}${PASS_COUNT}${NC}"
|
|
143
|
+
echo -e " Failed: ${RED}${FAIL_COUNT}${NC}"
|
|
144
|
+
echo ""
|
|
145
|
+
echo -e "${YELLOW}No modules to validate.${NC}"
|
|
146
|
+
exit 0
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
echo "Found ${#MODULES[@]} module(s): ${MODULES[*]}"
|
|
150
|
+
echo ""
|
|
151
|
+
|
|
152
|
+
# ── Required DDD layers ──
|
|
153
|
+
DDD_LAYERS=("domain" "application" "infrastructure" "interfaces")
|
|
154
|
+
|
|
155
|
+
# ── Check 1: Every module has the 4 DDD layers ──
|
|
156
|
+
echo "--- Layer Structure ---"
|
|
157
|
+
|
|
158
|
+
for module in "${MODULES[@]}"; do
|
|
159
|
+
module_dir="${SRC_DIR}/${module}"
|
|
160
|
+
missing=()
|
|
161
|
+
contracts_wrapper=false
|
|
162
|
+
|
|
163
|
+
# Check for contracts/ anti-pattern
|
|
164
|
+
if [[ -d "${module_dir}/contracts" ]]; then
|
|
165
|
+
contracts_wrapper=true
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
for layer in "${DDD_LAYERS[@]}"; do
|
|
169
|
+
if [[ ! -d "${module_dir}/${layer}" ]]; then
|
|
170
|
+
missing+=("$layer")
|
|
171
|
+
fi
|
|
172
|
+
done
|
|
173
|
+
|
|
174
|
+
if [[ ${#missing[@]} -eq 0 ]] && [[ "$contracts_wrapper" == false ]]; then
|
|
175
|
+
pass "${module} — all 4 DDD layers present"
|
|
176
|
+
elif [[ ${#missing[@]} -eq 0 ]] && [[ "$contracts_wrapper" == true ]]; then
|
|
177
|
+
fail "${module} — has contracts/ wrapper (should be domain/ application/ infrastructure/ interfaces/)"
|
|
178
|
+
else
|
|
179
|
+
fail "${module} — missing layers: ${missing[*]}"
|
|
180
|
+
fi
|
|
181
|
+
done
|
|
182
|
+
|
|
183
|
+
echo ""
|
|
184
|
+
|
|
185
|
+
# ── Check 2: No contracts/ wrapper ──
|
|
186
|
+
echo "--- Anti-Pattern: contracts/ Wrapper ---"
|
|
187
|
+
CONTRACTS_COUNT=0
|
|
188
|
+
for module in "${MODULES[@]}"; do
|
|
189
|
+
if [[ -d "${SRC_DIR}/${module}/contracts" ]]; then
|
|
190
|
+
CONTRACTS_COUNT=$((CONTRACTS_COUNT + 1))
|
|
191
|
+
|
|
192
|
+
# In --fix mode, suggest the rename
|
|
193
|
+
if [[ "$CHECK_MODE" == "fix" ]]; then
|
|
194
|
+
echo -e "${YELLOW} Would fix: git mv ${SRC_DIR}/${module}/contracts/* ${SRC_DIR}/${module}/ && rmdir ${SRC_DIR}/${module}/contracts${NC}"
|
|
195
|
+
fi
|
|
196
|
+
fi
|
|
197
|
+
done
|
|
198
|
+
|
|
199
|
+
if [[ $CONTRACTS_COUNT -eq 0 ]]; then
|
|
200
|
+
pass "No contracts/ wrapper directories found"
|
|
201
|
+
else
|
|
202
|
+
fail "Found ${CONTRACTS_COUNT} module(s) with contracts/ wrapper — should use direct DDD layers"
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
echo ""
|
|
206
|
+
|
|
207
|
+
# ── Check 3: Dependency direction (domain must not import outer layers) ──
|
|
208
|
+
echo "--- Dependency Direction ---"
|
|
209
|
+
VIOLATIONS=0
|
|
210
|
+
|
|
211
|
+
for module in "${MODULES[@]}"; do
|
|
212
|
+
domain_dir="${SRC_DIR}/${module}/domain"
|
|
213
|
+
if [[ ! -d "$domain_dir" ]]; then
|
|
214
|
+
continue
|
|
215
|
+
fi
|
|
216
|
+
|
|
217
|
+
# Find source files in domain/ and check for illegal imports
|
|
218
|
+
while IFS= read -r -d '' file; do
|
|
219
|
+
if grep -Eq "$IMPORT_PATTERN" "$file" 2>/dev/null; then
|
|
220
|
+
violations=$(grep -En "$IMPORT_PATTERN" "$file" 2>/dev/null || true)
|
|
221
|
+
if [[ -n "$violations" ]]; then
|
|
222
|
+
echo -e "${RED} Illegal import in ${file}${NC}"
|
|
223
|
+
while IFS= read -r line; do
|
|
224
|
+
echo " ${line}"
|
|
225
|
+
done <<< "$violations"
|
|
226
|
+
VIOLATIONS=$((VIOLATIONS + 1))
|
|
227
|
+
fi
|
|
228
|
+
fi
|
|
229
|
+
done < <(find "$domain_dir" -type f \( -name "*.${SRC_EXTS%% *}" $(for ext in ${SRC_EXTS#* }; do echo -o -name "*.$ext"; done) \) -print0 2>/dev/null)
|
|
230
|
+
done
|
|
231
|
+
|
|
232
|
+
if [[ $VIOLATIONS -eq 0 ]]; then
|
|
233
|
+
pass "No dependency direction violations (domain does not import outer layers)"
|
|
234
|
+
else
|
|
235
|
+
fail "Found ${VIOLATIONS} dependency direction violation(s) — domain/ must not import from outer layers"
|
|
236
|
+
fi
|
|
237
|
+
|
|
238
|
+
echo ""
|
|
239
|
+
|
|
240
|
+
# ── Check 4: Repository interfaces defined in domain, implemented in infrastructure ──
|
|
241
|
+
echo "--- Repository Pattern ---"
|
|
242
|
+
REPO_INFRA_ONLY=0
|
|
243
|
+
REPO_MISSING_INFRA=0
|
|
244
|
+
|
|
245
|
+
for module in "${MODULES[@]}"; do
|
|
246
|
+
infra_repo_dir="${SRC_DIR}/${module}/infrastructure/repository"
|
|
247
|
+
domain_repo_file=""
|
|
248
|
+
|
|
249
|
+
# Check for repository file in domain/
|
|
250
|
+
domain_repo=$(find "${SRC_DIR}/${module}/domain" -maxdepth 2 -name "*repository*" -o -name "*repo*" 2>/dev/null | head -1)
|
|
251
|
+
|
|
252
|
+
# Check for repository implementation in infrastructure/
|
|
253
|
+
infra_repo=$(find "${SRC_DIR}/${module}/infrastructure" -maxdepth 3 -name "*repository*" -o -name "*repo*" 2>/dev/null | head -1)
|
|
254
|
+
|
|
255
|
+
if [[ -n "$domain_repo" ]] && [[ -z "$infra_repo" ]]; then
|
|
256
|
+
warn "${module} — repository interface in domain/ but no implementation in infrastructure/"
|
|
257
|
+
REPO_MISSING_INFRA=$((REPO_MISSING_INFRA + 1))
|
|
258
|
+
fi
|
|
259
|
+
done
|
|
260
|
+
|
|
261
|
+
if [[ $REPO_MISSING_INFRA -eq 0 ]]; then
|
|
262
|
+
pass "Repository interfaces have implementations in infrastructure/ (where applicable)"
|
|
263
|
+
else
|
|
264
|
+
warn "${REPO_MISSING_INFRA} module(s) with unreferenced repository interfaces"
|
|
265
|
+
fi
|
|
266
|
+
|
|
267
|
+
echo ""
|
|
268
|
+
|
|
269
|
+
# ── Summary ──
|
|
270
|
+
echo "============================================"
|
|
271
|
+
echo " Summary"
|
|
272
|
+
echo "============================================"
|
|
273
|
+
echo -e " Language: ${BLUE}${PROJECT_LANG}${NC}"
|
|
274
|
+
echo -e " Modules: ${BLUE}${#MODULES[@]}${NC}"
|
|
275
|
+
echo -e " Passed: ${GREEN}${PASS_COUNT}${NC}"
|
|
276
|
+
echo -e " Failed: ${RED}${FAIL_COUNT}${NC}"
|
|
277
|
+
echo ""
|
|
278
|
+
|
|
279
|
+
if [[ ${#ERRORS[@]} -gt 0 ]]; then
|
|
280
|
+
echo "FAILURES:"
|
|
281
|
+
for err in "${ERRORS[@]}"; do
|
|
282
|
+
echo " - $err"
|
|
283
|
+
done
|
|
284
|
+
echo ""
|
|
285
|
+
echo -e "${RED}DDD structure violations found. Fix before merging.${NC}"
|
|
286
|
+
exit 1
|
|
287
|
+
fi
|
|
288
|
+
|
|
289
|
+
echo -e "${GREEN}All modules conform to DDD Clean Architecture layer structure.${NC}"
|
|
290
|
+
exit 0
|