ginskill-init 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/README.md +77 -0
  2. package/agents/developer.md +56 -0
  3. package/agents/frontend-design.md +69 -0
  4. package/agents/mobile-reviewer.md +36 -0
  5. package/agents/review-code.md +49 -0
  6. package/agents/security-scanner.md +50 -0
  7. package/agents/tester.md +72 -0
  8. package/bin/cli.js +226 -0
  9. package/package.json +20 -0
  10. package/skills/ai-asset-generator/SKILL.md +255 -0
  11. package/skills/ai-asset-generator/docs/gen-image.md +274 -0
  12. package/skills/ai-asset-generator/docs/genvideo.md +341 -0
  13. package/skills/ai-asset-generator/docs/remove-background.md +19 -0
  14. package/skills/ai-asset-generator/generate-credit-assets.mjs +180 -0
  15. package/skills/ai-asset-generator/generate-ginbrowser-assets.mjs +242 -0
  16. package/skills/ai-asset-generator/generate-sty-icon.mjs +149 -0
  17. package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
  18. package/skills/ai-asset-generator/lib/env.mjs +38 -0
  19. package/skills/ai-asset-generator/lib/kie-client.mjs +88 -0
  20. package/skills/ai-asset-generator/scripts/scaffold-generator.mjs +203 -0
  21. package/skills/ai-build-ai/SKILL.md +124 -0
  22. package/skills/ai-build-ai/docs/agent-teams.md +293 -0
  23. package/skills/ai-build-ai/docs/checkpointing.md +161 -0
  24. package/skills/ai-build-ai/docs/create-agent.md +399 -0
  25. package/skills/ai-build-ai/docs/create-mcp.md +395 -0
  26. package/skills/ai-build-ai/docs/create-skill.md +299 -0
  27. package/skills/ai-build-ai/docs/headless-mode.md +614 -0
  28. package/skills/ai-build-ai/docs/hooks.md +578 -0
  29. package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
  30. package/skills/ai-build-ai/docs/output-styles.md +208 -0
  31. package/skills/ai-build-ai/docs/overview.md +162 -0
  32. package/skills/ai-build-ai/docs/permissions.md +391 -0
  33. package/skills/ai-build-ai/docs/plugins.md +396 -0
  34. package/skills/ai-build-ai/docs/sandbox.md +262 -0
  35. package/skills/ai-build-ai/scripts/load-tutorial.sh +54 -0
  36. package/skills/icon-generator/SKILL.md +270 -0
  37. package/skills/mobile-app-review/SKILL.md +321 -0
  38. package/skills/mobile-app-review/references/apple-review.md +132 -0
  39. package/skills/mobile-app-review/references/google-play-review.md +203 -0
  40. package/skills/mongodb/SKILL.md +667 -0
  41. package/skills/mongodb/references/mongoose-patterns.md +368 -0
  42. package/skills/nestjs-architecture/SKILL.md +1086 -0
  43. package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
  44. package/skills/performance/SKILL.md +509 -0
  45. package/skills/react-fsd-architecture/SKILL.md +693 -0
  46. package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
  47. package/skills/react-query/SKILL.md +685 -0
  48. package/skills/react-query/references/query-patterns.md +365 -0
  49. package/skills/review-code/SKILL.md +321 -0
  50. package/skills/review-code/references/clean-code-principles.md +395 -0
  51. package/skills/review-code/references/frontend-patterns.md +136 -0
  52. package/skills/review-code/references/nestjs-patterns.md +184 -0
  53. package/skills/review-code/scripts/check-module.sh +201 -0
  54. package/skills/review-code/scripts/deep-scan.sh +604 -0
  55. package/skills/review-code/scripts/dep-check.sh +522 -0
  56. package/skills/review-code/scripts/detect-duplicates.sh +466 -0
  57. package/skills/review-code/scripts/format-check.sh +577 -0
  58. package/skills/review-code/scripts/run-review.sh +167 -0
  59. package/skills/review-code/scripts/scan-codebase.sh +152 -0
  60. package/skills/security-scanner/SKILL.md +327 -0
  61. package/skills/security-scanner/references/nestjs-security.md +260 -0
  62. package/skills/security-scanner/references/nextjs-security.md +201 -0
  63. package/skills/security-scanner/references/react-native-security.md +199 -0
  64. package/skills/security-scanner/scripts/security-scan.sh +478 -0
  65. package/skills/ui-ux-pro-max/SKILL.md +377 -0
  66. package/skills/ui-ux-pro-max/data/charts.csv +26 -0
  67. package/skills/ui-ux-pro-max/data/colors.csv +97 -0
  68. package/skills/ui-ux-pro-max/data/icons.csv +101 -0
  69. package/skills/ui-ux-pro-max/data/landing.csv +31 -0
  70. package/skills/ui-ux-pro-max/data/products.csv +97 -0
  71. package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  72. package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  73. package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  74. package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  75. package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  76. package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  77. package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  78. package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  79. package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  80. package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  81. package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  82. package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  83. package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  84. package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  85. package/skills/ui-ux-pro-max/data/styles.csv +68 -0
  86. package/skills/ui-ux-pro-max/data/typography.csv +58 -0
  87. package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  88. package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  89. package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  90. package/skills/ui-ux-pro-max/scripts/core.py +253 -0
  91. package/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  92. package/skills/ui-ux-pro-max/scripts/search.py +114 -0
@@ -0,0 +1,522 @@
1
+ #!/usr/bin/env bash
2
+ # ─────────────────────────────────────────────────────────────
3
+ # Dependency & Import Analyzer
4
+ #
5
+ # Detects circular dependencies, unused exports, orphan files,
6
+ # and import boundary violations. Pure bash — no npm deps.
7
+ #
8
+ # Usage:
9
+ # ./dep-check.sh # scan all projects
10
+ # ./dep-check.sh backend # scan backend only
11
+ # ./dep-check.sh frontend # scan frontend only
12
+ # ./dep-check.sh mobile # scan mobile only
13
+ # ./dep-check.sh --check circular # only circular deps
14
+ # ./dep-check.sh --check unused # only unused exports
15
+ # ./dep-check.sh --check orphans # only orphan files
16
+ # ./dep-check.sh --check boundaries # only import boundary violations
17
+ # ./dep-check.sh --summary # summary counts only
18
+ #
19
+ # Output: JSON to stdout
20
+ # Exit codes: 0 = clean, 1 = issues found, 2 = critical issues
21
+ # ─────────────────────────────────────────────────────────────
22
+
23
+ set -uo pipefail
24
+
25
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
26
+ REPO_ROOT="$(cd "$SCRIPT_DIR/../../../../.." && pwd)"
27
+
28
+ # ─── Defaults ────────────────────────────────────────────────
29
+ TARGET="all"
30
+ CHECK="all"
31
+ SUMMARY_ONLY=false
32
+
33
+ # ─── Auto-detect project directories ────────────────────────
34
+ detect_src() {
35
+ local kind="$1"; shift
36
+ for candidate in "$@"; do
37
+ for dir in $REPO_ROOT/$candidate; do
38
+ if [ -d "$dir/src" ]; then echo "$dir/src"; return; fi
39
+ if [ -d "$dir/app" ]; then echo "$dir/app"; return; fi
40
+ done
41
+ done
42
+ echo ""
43
+ }
44
+
45
+ detect_root() {
46
+ local kind="$1"; shift
47
+ for candidate in "$@"; do
48
+ for dir in $REPO_ROOT/$candidate; do
49
+ if [ -d "$dir" ] && [ -f "$dir/package.json" ]; then echo "$dir"; return; fi
50
+ done
51
+ done
52
+ echo ""
53
+ }
54
+
55
+ BE_SRC="$(detect_src backend be-* backend server api)"
56
+ FE_SRC="$(detect_src frontend web-* frontend client)"
57
+ MB_SRC="$(detect_src mobile styai-mobile mobile)"
58
+ AUTH_SRC=""
59
+ [ -d "$REPO_ROOT/auth-package/src" ] && AUTH_SRC="$REPO_ROOT/auth-package/src"
60
+
61
+ BE_ROOT="$(detect_root backend be-* backend server api)"
62
+ FE_ROOT="$(detect_root frontend web-* frontend client)"
63
+ MB_ROOT="$(detect_root mobile styai-mobile mobile)"
64
+
65
+ # ─── Parse arguments ────────────────────────────────────────
66
+ while [[ $# -gt 0 ]]; do
67
+ case "$1" in
68
+ backend) TARGET="backend"; shift ;;
69
+ frontend) TARGET="frontend"; shift ;;
70
+ mobile) TARGET="mobile"; shift ;;
71
+ auth) TARGET="auth"; shift ;;
72
+ --check) CHECK="$2"; shift 2 ;;
73
+ --summary) SUMMARY_ONLY=true; shift ;;
74
+ *) shift ;;
75
+ esac
76
+ done
77
+
78
+ # ─── Build file list ────────────────────────────────────────
79
+ TMPDIR_WORK=$(mktemp -d)
80
+ trap 'rm -rf "$TMPDIR_WORK"' EXIT
81
+
82
+ FILES_LIST="$TMPDIR_WORK/files.txt"
83
+ FINDINGS_FILE="$TMPDIR_WORK/findings.jsonl"
84
+ touch "$FINDINGS_FILE"
85
+
86
+ build_file_list() {
87
+ local dirs=""
88
+ case "$TARGET" in
89
+ backend) [ -n "$BE_SRC" ] && dirs="$BE_SRC" ;;
90
+ frontend) [ -n "$FE_SRC" ] && dirs="$FE_SRC" ;;
91
+ mobile) [ -n "$MB_SRC" ] && dirs="$MB_SRC" ;;
92
+ auth) [ -n "$AUTH_SRC" ] && dirs="$AUTH_SRC" ;;
93
+ all)
94
+ [ -n "$BE_SRC" ] && dirs="$BE_SRC"
95
+ [ -n "$FE_SRC" ] && dirs="$dirs $FE_SRC"
96
+ [ -n "$MB_SRC" ] && dirs="$dirs $MB_SRC"
97
+ [ -n "$AUTH_SRC" ] && dirs="$dirs $AUTH_SRC"
98
+ ;;
99
+ esac
100
+ dirs=$(echo "$dirs" | xargs)
101
+ [ -z "$dirs" ] && return
102
+ for dir in $dirs; do
103
+ find "$dir" \( -name "*.ts" -o -name "*.tsx" \) \
104
+ -not -path "*/node_modules/*" \
105
+ -not -path "*/.next/*" \
106
+ -not -path "*/dist/*" \
107
+ -not -path "*.d.ts" \
108
+ 2>/dev/null
109
+ done
110
+ }
111
+
112
+ build_file_list | sort > "$FILES_LIST"
113
+ FILE_COUNT=$(wc -l < "$FILES_LIST" | tr -d ' ')
114
+
115
+ if [ "$FILE_COUNT" -eq 0 ]; then
116
+ echo '{"scan":"dep-check","findings":[],"summary":{"total":0}}'
117
+ exit 0
118
+ fi
119
+
120
+ # ─── Helpers ────────────────────────────────────────────────
121
+ relpath() { echo "${1#$REPO_ROOT/}"; }
122
+
123
+ json_escape() {
124
+ printf '%s' "$1" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read()), end="")' 2>/dev/null || \
125
+ printf '"%s"' "$(printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\t/\\t/g')"
126
+ }
127
+
128
+ count_matches() {
129
+ local result
130
+ result=$(grep -c "$1" "$2" 2>/dev/null) || true
131
+ echo "${result:-0}"
132
+ }
133
+
134
+ add_finding() {
135
+ local check="$1" severity="$2" rule="$3" file="$4" line="$5" message="$6" suggestion="$7"
136
+ if [ "$CHECK" != "all" ] && [ "$CHECK" != "$check" ]; then return; fi
137
+ echo "{\"check\":\"$check\",\"severity\":\"$severity\",\"rule\":\"$rule\",\"file\":\"$(relpath "$file")\",\"line\":$line,\"message\":$(json_escape "$message"),\"suggestion\":$(json_escape "$suggestion")}" >> "$FINDINGS_FILE"
138
+ }
139
+
140
+ # ═══════════════════════════════════════════════════════════════
141
+ # 1. CIRCULAR DEPENDENCY DETECTION
142
+ # ═══════════════════════════════════════════════════════════════
143
+ check_circular() {
144
+ local import_map="$TMPDIR_WORK/import_map.txt"
145
+
146
+ # Build import map: file -> imported_file
147
+ while IFS= read -r file; do
148
+ local dir
149
+ dir=$(dirname "$file")
150
+ # Extract import paths (relative imports only)
151
+ grep -n "^import.*from ['\"]\./" "$file" 2>/dev/null | while IFS=: read -r lineno content; do
152
+ # Extract the path
153
+ local import_path
154
+ import_path=$(echo "$content" | sed "s/.*from ['\"]//; s/['\"].*//" )
155
+ [ -z "$import_path" ] && continue
156
+
157
+ # Resolve to absolute file path
158
+ local resolved=""
159
+ for ext in "" ".ts" ".tsx" "/index.ts" "/index.tsx"; do
160
+ local candidate="$dir/$import_path$ext"
161
+ candidate=$(cd "$(dirname "$candidate")" 2>/dev/null && echo "$(pwd)/$(basename "$candidate")")
162
+ if [ -f "$candidate" ]; then
163
+ resolved="$candidate"
164
+ break
165
+ fi
166
+ done
167
+
168
+ [ -n "$resolved" ] && echo "$(relpath "$file") $(relpath "$resolved")"
169
+ done
170
+ done < "$FILES_LIST" > "$import_map"
171
+
172
+ # Detect cycles: A->B->A (direct circular)
173
+ sort "$import_map" | awk -F'\t' '
174
+ {
175
+ from = $1; to = $2
176
+ edges[from] = edges[from] " " to
177
+ # Check for direct cycle: A imports B AND B imports A
178
+ if (from == to) {
179
+ printf "self\t%s\t%s\n", from, from
180
+ }
181
+ }
182
+ END {
183
+ # Check bidirectional imports
184
+ for (a in edges) {
185
+ n = split(edges[a], targets, " ")
186
+ for (i = 1; i <= n; i++) {
187
+ b = targets[i]
188
+ if (b == "" || b == a) continue
189
+ # Check if b also imports a
190
+ m = split(edges[b], btargets, " ")
191
+ for (j = 1; j <= m; j++) {
192
+ if (btargets[j] == a) {
193
+ # Only print once (alphabetically first)
194
+ if (a < b) {
195
+ printf "mutual\t%s\t%s\n", a, b
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
202
+ ' | sort -u | while IFS=$'\t' read -r kind file_a file_b; do
203
+ if [ "$kind" = "self" ]; then
204
+ add_finding "circular" "critical" "self_import" "$REPO_ROOT/$file_a" 1 \
205
+ "File imports itself" \
206
+ "Remove the self-referencing import"
207
+ else
208
+ add_finding "circular" "warning" "mutual_import" "$REPO_ROOT/$file_a" 1 \
209
+ "Circular dependency: $file_a <-> $file_b" \
210
+ "Break the cycle by extracting shared logic into a third module, or use dependency inversion"
211
+ fi
212
+ done
213
+
214
+ # Detect feature-to-feature cross-imports (NestJS modules importing each other)
215
+ if [ -n "$BE_SRC" ] && [ -d "$BE_SRC/features" ]; then
216
+ grep "features/" "$import_map" 2>/dev/null | awk -F'\t' '
217
+ {
218
+ from = $1; to = $2
219
+ # Extract feature names
220
+ split(from, from_parts, "/")
221
+ split(to, to_parts, "/")
222
+ from_feature = ""; to_feature = ""
223
+ for (i = 1; i <= length(from_parts); i++) {
224
+ if (from_parts[i] == "features" && i < length(from_parts)) { from_feature = from_parts[i+1]; break }
225
+ }
226
+ for (i = 1; i <= length(to_parts); i++) {
227
+ if (to_parts[i] == "features" && i < length(to_parts)) { to_feature = to_parts[i+1]; break }
228
+ }
229
+ if (from_feature != "" && to_feature != "" && from_feature != to_feature) {
230
+ key = from_feature "\t" to_feature
231
+ if (!(key in seen)) {
232
+ seen[key] = 1
233
+ printf "%s\t%s\t%s\n", from_feature, to_feature, from
234
+ }
235
+ }
236
+ }
237
+ ' | while IFS=$'\t' read -r from_feat to_feat file; do
238
+ add_finding "circular" "info" "cross_feature_import" "$REPO_ROOT/$file" 1 \
239
+ "Feature '$from_feat' imports from feature '$to_feat'" \
240
+ "Feature modules should communicate through shared interfaces, not import directly from each other"
241
+ done
242
+ fi
243
+ }
244
+
245
+ # ═══════════════════════════════════════════════════════════════
246
+ # 2. UNUSED EXPORTS DETECTION
247
+ # ═══════════════════════════════════════════════════════════════
248
+ check_unused() {
249
+ local exports_file="$TMPDIR_WORK/exports.txt"
250
+ local imports_file="$TMPDIR_WORK/all_imports.txt"
251
+
252
+ # Collect all exported symbols
253
+ while IFS= read -r file; do
254
+ # Named exports: export const/function/class/type/interface/enum NAME
255
+ grep -n "^export " "$file" 2>/dev/null | \
256
+ grep -v "^export default\|^export \*\|^export {" | \
257
+ sed 's/.*export \(const\|let\|var\|function\|class\|type\|interface\|enum\|async function\) //' | \
258
+ sed 's/[^a-zA-Z0-9_].*//' | while read -r name; do
259
+ [ -n "$name" ] && echo "$name $(relpath "$file")"
260
+ done
261
+
262
+ # Re-exports from index files are not counted as "unused"
263
+ done < "$FILES_LIST" > "$exports_file"
264
+
265
+ # Collect all import references across all files
266
+ while IFS= read -r file; do
267
+ grep "^import " "$file" 2>/dev/null | \
268
+ sed 's/.*{//; s/}.*//' | tr ',' '\n' | \
269
+ sed 's/[[:space:]]*as[[:space:]].*//; s/^[[:space:]]*//; s/[[:space:]]*$//' | \
270
+ grep -v '^$' | while read -r name; do
271
+ echo "$name"
272
+ done
273
+ done < "$FILES_LIST" | sort -u > "$imports_file"
274
+
275
+ # Find exports that are never imported anywhere
276
+ while IFS=$'\t' read -r name file; do
277
+ [ -z "$name" ] && continue
278
+ # Skip common framework exports (decorators, modules, etc.)
279
+ case "$name" in
280
+ Module|Controller|Service|Guard|Pipe|Filter|Interceptor|Gateway) continue ;;
281
+ App*|Main|bootstrap) continue ;;
282
+ esac
283
+
284
+ # Check if this export is referenced in any other file
285
+ if ! grep -q "^${name}$" "$imports_file" 2>/dev/null; then
286
+ # Double check: is it used in a dynamic import, decorator, or non-standard way?
287
+ local used
288
+ used=$(grep -rl "\b${name}\b" $(cat "$FILES_LIST") 2>/dev/null | grep -v "^${REPO_ROOT}/${file}$" | head -1)
289
+ if [ -z "$used" ]; then
290
+ local lineno
291
+ lineno=$(grep -n "export.*\b${name}\b" "$REPO_ROOT/$file" 2>/dev/null | head -1 | cut -d: -f1)
292
+ lineno=${lineno:-1}
293
+ add_finding "unused" "info" "unused_export" "$REPO_ROOT/$file" "$lineno" \
294
+ "Exported symbol '$name' is not imported by any other file" \
295
+ "Remove the export keyword if only used internally, or delete if truly unused"
296
+ fi
297
+ fi
298
+ done < "$exports_file"
299
+ }
300
+
301
+ # ═══════════════════════════════════════════════════════════════
302
+ # 3. ORPHAN FILE DETECTION
303
+ # ═══════════════════════════════════════════════════════════════
304
+ check_orphans() {
305
+ # Files that are never imported by any other file (potential dead code)
306
+ local all_imports="$TMPDIR_WORK/all_import_paths.txt"
307
+
308
+ # Collect all import targets
309
+ while IFS= read -r file; do
310
+ grep "^import.*from ['\"]" "$file" 2>/dev/null | \
311
+ sed "s/.*from ['\"]//; s/['\"].*//" | while read -r import_path; do
312
+ echo "$import_path"
313
+ done
314
+ done < "$FILES_LIST" | sort -u > "$all_imports"
315
+
316
+ # Check each file
317
+ while IFS= read -r file; do
318
+ local rel
319
+ rel=$(relpath "$file")
320
+ local basename_no_ext
321
+ basename_no_ext=$(basename "$file" | sed 's/\.[^.]*$//')
322
+
323
+ # Skip entry points and special files
324
+ case "$basename_no_ext" in
325
+ main|index|app|App|layout|page|loading|error|not-found) continue ;;
326
+ *.module|*.spec|*.test|*.config|*.d) continue ;;
327
+ _*) continue ;; # Convention: _ prefix = internal
328
+ esac
329
+ [[ "$file" == *"/index."* ]] && continue
330
+ [[ "$file" == *".module."* ]] && continue
331
+ [[ "$file" == *".spec."* || "$file" == *".test."* ]] && continue
332
+ [[ "$file" == *".config."* ]] && continue
333
+
334
+ # Check if this file is imported by anything
335
+ # Try various import path patterns
336
+ local found=false
337
+ local rel_no_ext
338
+ rel_no_ext=$(echo "$rel" | sed 's/\.[^.]*$//')
339
+
340
+ # Check if any import path matches this file
341
+ if grep -qF "$basename_no_ext" "$all_imports" 2>/dev/null; then
342
+ found=true
343
+ fi
344
+
345
+ # Also check for directory-relative imports that could point to this file
346
+ if [ "$found" = false ]; then
347
+ local dir_name
348
+ dir_name=$(basename "$(dirname "$file")")
349
+ if grep -qF "$dir_name/$basename_no_ext" "$all_imports" 2>/dev/null; then
350
+ found=true
351
+ fi
352
+ fi
353
+
354
+ if [ "$found" = false ]; then
355
+ add_finding "orphans" "info" "orphan_file" "$file" 1 \
356
+ "File '$(basename "$file")' is not imported by any other file" \
357
+ "Verify this file is used (may be dynamically loaded, or could be dead code to remove)"
358
+ fi
359
+ done < "$FILES_LIST"
360
+ }
361
+
362
+ # ═══════════════════════════════════════════════════════════════
363
+ # 4. IMPORT BOUNDARY VIOLATIONS
364
+ # ═══════════════════════════════════════════════════════════════
365
+ check_boundaries() {
366
+ while IFS= read -r file; do
367
+ local rel
368
+ rel=$(relpath "$file")
369
+
370
+ # Rule 1: Don't import from node_modules internals (deep imports)
371
+ grep -n "from '[^']*node_modules" "$file" 2>/dev/null | while IFS=: read -r lineno content; do
372
+ add_finding "boundaries" "warning" "node_modules_deep_import" "$file" "$lineno" \
373
+ "Direct import from node_modules path" \
374
+ "Import from the package name, not from node_modules internal path"
375
+ done
376
+
377
+ # Rule 2: Don't import from parent project (e.g., mobile importing from backend)
378
+ if [[ "$file" == *"/styai-mobile/"* ]]; then
379
+ grep -n "from '.*be-easycloset\|from '.*web-ui-easycloset" "$file" 2>/dev/null | while IFS=: read -r lineno content; do
380
+ add_finding "boundaries" "critical" "cross_project_import" "$file" "$lineno" \
381
+ "Mobile app imports directly from another project" \
382
+ "Use shared packages (auth-package) or API calls instead of direct imports"
383
+ done
384
+ fi
385
+
386
+ if [[ "$file" == *"/web-ui-easycloset/"* ]]; then
387
+ grep -n "from '.*be-easycloset\|from '.*styai-mobile" "$file" 2>/dev/null | while IFS=: read -r lineno content; do
388
+ add_finding "boundaries" "critical" "cross_project_import" "$file" "$lineno" \
389
+ "Frontend imports directly from another project" \
390
+ "Use shared packages or API calls instead of direct imports"
391
+ done
392
+ fi
393
+
394
+ # Rule 3: Don't import test utilities in production code
395
+ [[ "$file" == *".spec."* || "$file" == *".test."* ]] && continue
396
+ grep -n "from '.*__tests__\|from '.*\.spec\|from '.*\.test\|from '@testing-library\|from 'jest'" "$file" 2>/dev/null | while IFS=: read -r lineno content; do
397
+ add_finding "boundaries" "warning" "test_import_in_prod" "$file" "$lineno" \
398
+ "Test utility imported in production code" \
399
+ "Move test-related code to test files"
400
+ done
401
+
402
+ # Rule 4: Don't use relative imports that go too far up (../../../..)
403
+ grep -n "from '\.\./\.\./\.\./\.\." "$file" 2>/dev/null | while IFS=: read -r lineno content; do
404
+ add_finding "boundaries" "info" "deep_relative_import" "$file" "$lineno" \
405
+ "Deep relative import (4+ levels up)" \
406
+ "Use path aliases (@/, @features/, @shared/) instead of deep relative imports"
407
+ done
408
+
409
+ # Rule 5: Backend internal imports should use path aliases
410
+ if [[ "$file" == *"/be-easycloset/src/"* ]]; then
411
+ grep -n "from '\.\./\.\./\.\." "$file" 2>/dev/null | \
412
+ grep -v "\.spec\.\|\.test\." | while IFS=: read -r lineno content; do
413
+ add_finding "boundaries" "info" "missing_path_alias" "$file" "$lineno" \
414
+ "Deep relative import — consider using path alias" \
415
+ "Use @features/, @shared/, @core/ path aliases instead of ../../.."
416
+ done
417
+ fi
418
+
419
+ done < "$FILES_LIST"
420
+ }
421
+
422
+ # ═══════════════════════════════════════════════════════════════
423
+ # RUN CHECKS
424
+ # ═══════════════════════════════════════════════════════════════
425
+
426
+ if [ "$CHECK" = "all" ] || [ "$CHECK" = "circular" ]; then
427
+ check_circular
428
+ fi
429
+
430
+ if [ "$CHECK" = "all" ] || [ "$CHECK" = "unused" ]; then
431
+ check_unused
432
+ fi
433
+
434
+ if [ "$CHECK" = "all" ] || [ "$CHECK" = "orphans" ]; then
435
+ check_orphans
436
+ fi
437
+
438
+ if [ "$CHECK" = "all" ] || [ "$CHECK" = "boundaries" ]; then
439
+ check_boundaries
440
+ fi
441
+
442
+ # ═══════════════════════════════════════════════════════════════
443
+ # OUTPUT
444
+ # ═══════════════════════════════════════════════════════════════
445
+
446
+ TOTAL=$(wc -l < "$FINDINGS_FILE" | tr -d ' ')
447
+ CRITICAL=$(count_matches '"severity":"critical"' "$FINDINGS_FILE")
448
+ WARNINGS=$(count_matches '"severity":"warning"' "$FINDINGS_FILE")
449
+ INFO=$(count_matches '"severity":"info"' "$FINDINGS_FILE")
450
+
451
+ CIRCULAR_COUNT=$(count_matches '"check":"circular"' "$FINDINGS_FILE")
452
+ UNUSED_COUNT=$(count_matches '"check":"unused"' "$FINDINGS_FILE")
453
+ ORPHAN_COUNT=$(count_matches '"check":"orphans"' "$FINDINGS_FILE")
454
+ BOUNDARY_COUNT=$(count_matches '"check":"boundaries"' "$FINDINGS_FILE")
455
+
456
+ if [ "$SUMMARY_ONLY" = true ]; then
457
+ cat <<EOF
458
+ {
459
+ "scan": "dep-check",
460
+ "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
461
+ "target": "$TARGET",
462
+ "files_scanned": $FILE_COUNT,
463
+ "summary": {
464
+ "total": $TOTAL,
465
+ "critical": $CRITICAL,
466
+ "warning": $WARNINGS,
467
+ "info": $INFO,
468
+ "by_check": {
469
+ "circular": $CIRCULAR_COUNT,
470
+ "unused": $UNUSED_COUNT,
471
+ "orphans": $ORPHAN_COUNT,
472
+ "boundaries": $BOUNDARY_COUNT
473
+ }
474
+ }
475
+ }
476
+ EOF
477
+ else
478
+ {
479
+ echo '{'
480
+ echo ' "scan": "dep-check",'
481
+ echo ' "timestamp": "'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'",'
482
+ echo ' "target": "'"$TARGET"'",'
483
+ echo ' "files_scanned": '"$FILE_COUNT"','
484
+ echo ' "summary": {'
485
+ echo ' "total": '"$TOTAL"','
486
+ echo ' "critical": '"$CRITICAL"','
487
+ echo ' "warning": '"$WARNINGS"','
488
+ echo ' "info": '"$INFO"','
489
+ echo ' "by_check": {'
490
+ echo ' "circular": '"$CIRCULAR_COUNT"','
491
+ echo ' "unused": '"$UNUSED_COUNT"','
492
+ echo ' "orphans": '"$ORPHAN_COUNT"','
493
+ echo ' "boundaries": '"$BOUNDARY_COUNT"
494
+ echo ' }'
495
+ echo ' },'
496
+ echo ' "findings": ['
497
+
498
+ first=true
499
+ while IFS= read -r line; do
500
+ [ -z "$line" ] && continue
501
+ if [ "$first" = true ]; then
502
+ first=false
503
+ else
504
+ echo ','
505
+ fi
506
+ printf ' %s' "$line"
507
+ done < "$FINDINGS_FILE"
508
+
509
+ echo ''
510
+ echo ' ]'
511
+ echo '}'
512
+ }
513
+ fi
514
+
515
+ # ─── Exit code ──────────────────────────────────────────────
516
+ if [ "$CRITICAL" -gt 0 ]; then
517
+ exit 2
518
+ elif [ "$TOTAL" -gt 0 ]; then
519
+ exit 1
520
+ else
521
+ exit 0
522
+ fi