@pipemd-core/pipemd 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 (96) hide show
  1. package/AI_SETUP_PIPEMD.md +184 -0
  2. package/CHANGELOG.md +47 -0
  3. package/LICENSE +15 -0
  4. package/README.md +535 -0
  5. package/dist/index.js +6647 -0
  6. package/dist/plugins/opencode-server.js +235 -0
  7. package/dist/plugins/opencode-tui.js +914 -0
  8. package/dist/templates/agent-decision-tree.md +113 -0
  9. package/dist/templates/static-rules.md +7 -0
  10. package/package.json +68 -0
  11. package/scripts/C-CPP/architecture/arch.sh +229 -0
  12. package/scripts/C-CPP/lib/limit.sh +146 -0
  13. package/scripts/C-CPP/project/class-diagram.sh +96 -0
  14. package/scripts/C-CPP/project/cmake-targets.sh +68 -0
  15. package/scripts/C-CPP/project/deps.sh +44 -0
  16. package/scripts/C-CPP/project/find-todos.sh +6 -0
  17. package/scripts/C-CPP/project/include-graph.sh +110 -0
  18. package/scripts/C-CPP/project/interfaces.sh +108 -0
  19. package/scripts/C-CPP/project/tree.sh +5 -0
  20. package/scripts/C-CPP/quality/lint.sh +14 -0
  21. package/scripts/C-CPP/quality/test-summary.sh +22 -0
  22. package/scripts/C-CPP/quality/type-check.sh +26 -0
  23. package/scripts/DevOps/architecture/arch.sh +186 -0
  24. package/scripts/DevOps/devops/aws-context.sh +34 -0
  25. package/scripts/DevOps/devops/docker-stats.sh +42 -0
  26. package/scripts/DevOps/devops/k8s-unhealthy.sh +41 -0
  27. package/scripts/DevOps/devops/tf-state.sh +65 -0
  28. package/scripts/DevOps/lib/limit.sh +143 -0
  29. package/scripts/Generic/architecture/arch.sh +570 -0
  30. package/scripts/Generic/lib/limit.sh +140 -0
  31. package/scripts/Go/architecture/arch.sh +79 -0
  32. package/scripts/Go/lib/limit.sh +142 -0
  33. package/scripts/Go/project/deps.sh +35 -0
  34. package/scripts/Go/project/find-todos.sh +6 -0
  35. package/scripts/Go/project/go-interfaces.sh +18 -0
  36. package/scripts/Go/project/go-packages.sh +28 -0
  37. package/scripts/Go/project/tree.sh +5 -0
  38. package/scripts/Go/quality/lint.sh +16 -0
  39. package/scripts/Go/quality/test-summary.sh +16 -0
  40. package/scripts/Go/quality/type-check.sh +16 -0
  41. package/scripts/Node-TypeScript/api/express-routes.sh +14 -0
  42. package/scripts/Node-TypeScript/api/nest-controllers.sh +18 -0
  43. package/scripts/Node-TypeScript/architecture/arch.sh +174 -0
  44. package/scripts/Node-TypeScript/frontend/angular-routes.sh +15 -0
  45. package/scripts/Node-TypeScript/frontend/nextjs-app-router.sh +13 -0
  46. package/scripts/Node-TypeScript/frontend/react-components.sh +20 -0
  47. package/scripts/Node-TypeScript/lib/limit.sh +146 -0
  48. package/scripts/Node-TypeScript/project/deps.sh +15 -0
  49. package/scripts/Node-TypeScript/project/find-todos.sh +6 -0
  50. package/scripts/Node-TypeScript/quality/lint.sh +10 -0
  51. package/scripts/Node-TypeScript/quality/test-summary.sh +39 -0
  52. package/scripts/Node-TypeScript/quality/type-check.sh +10 -0
  53. package/scripts/Python/api/fastapi-routes.sh +12 -0
  54. package/scripts/Python/architecture/arch.sh +220 -0
  55. package/scripts/Python/db/django-models.sh +12 -0
  56. package/scripts/Python/db/sqlalchemy.sh +17 -0
  57. package/scripts/Python/lib/limit.sh +144 -0
  58. package/scripts/Python/project/deps.sh +28 -0
  59. package/scripts/Python/project/find-todos.sh +6 -0
  60. package/scripts/Python/quality/lint.sh +13 -0
  61. package/scripts/Python/quality/test-summary.sh +11 -0
  62. package/scripts/Python/quality/type-check.sh +10 -0
  63. package/scripts/Rust/architecture/arch.sh +176 -0
  64. package/scripts/Rust/lib/limit.sh +142 -0
  65. package/scripts/Rust/project/cargo-deps.sh +42 -0
  66. package/scripts/Rust/project/cargo-features.sh +26 -0
  67. package/scripts/Rust/project/find-todos.sh +6 -0
  68. package/scripts/Rust/project/tree.sh +5 -0
  69. package/scripts/Rust/quality/lint.sh +16 -0
  70. package/scripts/Rust/quality/test-summary.sh +16 -0
  71. package/scripts/Rust/quality/type-check.sh +16 -0
  72. package/scripts/Shared/api/express-routes.sh +11 -0
  73. package/scripts/Shared/api/fastapi-routes.sh +10 -0
  74. package/scripts/Shared/api/nest-controllers.sh +22 -0
  75. package/scripts/Shared/architecture/normalize.sh +178 -0
  76. package/scripts/Shared/crew/crew.sh +15 -0
  77. package/scripts/Shared/db/django-models.sh +11 -0
  78. package/scripts/Shared/db/prisma.sh +33 -0
  79. package/scripts/Shared/db/sqlalchemy.sh +12 -0
  80. package/scripts/Shared/frontend/angular-routes.sh +11 -0
  81. package/scripts/Shared/frontend/nextjs-app-router.sh +13 -0
  82. package/scripts/Shared/frontend/react-components.sh +11 -0
  83. package/scripts/Shared/git/diff-stat.sh +6 -0
  84. package/scripts/Shared/git/git-branch.sh +16 -0
  85. package/scripts/Shared/git/git-log.sh +6 -0
  86. package/scripts/Shared/git/git-status.sh +6 -0
  87. package/scripts/Shared/lib/limit.sh +144 -0
  88. package/scripts/Shared/project/compose-md.sh +182 -0
  89. package/scripts/Shared/project/deps.sh +69 -0
  90. package/scripts/Shared/project/find-todos.sh +6 -0
  91. package/scripts/Shared/project/tree.sh +5 -0
  92. package/scripts/Shared/quality/lint.sh +81 -0
  93. package/scripts/Shared/quality/test-summary.sh +103 -0
  94. package/scripts/Shared/quality/type-check.sh +114 -0
  95. package/scripts/copy-plugins.mjs +4 -0
  96. package/scripts/copy-templates.mjs +5 -0
@@ -0,0 +1,570 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # Architecture map — Generic module dependencies (ecosystem-agnostic fallback)
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+
6
+ : "${MAX_ARCH:=100}"
7
+
8
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
9
+ NORMALIZE="$SCRIPT_DIR/normalize.sh"
10
+ [ -f "$NORMALIZE" ] || NORMALIZE="$SCRIPT_DIR/../../Shared/architecture/normalize.sh"
11
+
12
+ EXCLUDE_DIRS='node_modules|\.git|\.pipemd|dist|build|coverage|venv|\.venv|__pycache__|target|\.next|\.nuxt|out|bin|obj|\.cache|vendor|bower_components'
13
+
14
+ SOURCE_EXTS='\.js$|\.ts$|\.jsx$|\.tsx$|\.py$|\.go$|\.rs$|\.c$|\.cpp$|\.cc$|\.cxx$|\.h$|\.hpp$|\.java$|\.rb$|\.php$|\.cs$|\.swift$|\.kt$|\.scala$|\.sh$|\.mjs$|\.cjs$'
15
+
16
+ PROJECT_ROOT="$(pwd)"
17
+
18
+ top_modules() {
19
+ local modules=""
20
+ for entry in "$PROJECT_ROOT"/*; do
21
+ [ -e "$entry" ] || continue
22
+ local name
23
+ name=$(basename "$entry")
24
+ echo "$name" | grep -qE "^($EXCLUDE_DIRS)$" && continue
25
+ echo "$name" | grep -qE '^\.' && continue
26
+ if [ -d "$entry" ]; then
27
+ modules="$modules $name"
28
+ fi
29
+ done
30
+ echo "$modules"
31
+ }
32
+
33
+ MODULE_LIST=$(top_modules)
34
+
35
+ is_internal_dir() {
36
+ local name="$1"
37
+ echo "$MODULE_LIST" | grep -qw "$name"
38
+ }
39
+
40
+ find_source_files() {
41
+ find . -maxdepth 3 -type f -print 2>/dev/null | while IFS= read -r f; do
42
+ echo "$f" | grep -qE "/($EXCLUDE_DIRS)(/|$)" && continue
43
+ echo "$f" | grep -qE "$SOURCE_EXTS" && echo "$f"
44
+ done
45
+ }
46
+
47
+ file_to_module() {
48
+ local filepath="$1"
49
+ local relpath="${filepath#./}"
50
+ local dirpart
51
+ dirpart=$(dirname "$relpath")
52
+
53
+ if [ "$dirpart" = "." ]; then
54
+ basename "$relpath" | sed 's/\.[^.]*$//'
55
+ return
56
+ fi
57
+
58
+ local topdir
59
+ topdir=$(echo "$dirpart" | cut -d/ -f1)
60
+ if is_internal_dir "$topdir"; then
61
+ echo "$topdir"
62
+ else
63
+ echo "$dirpart" | awk -F/ '{print $NF}'
64
+ fi
65
+ }
66
+
67
+ EXTERNAL_DEPS=""
68
+ collect_external_deps() {
69
+ local deps=""
70
+
71
+ if [ -f package.json ]; then
72
+ local js_deps
73
+ js_deps=$(python3 -c "
74
+ import json, sys
75
+ try:
76
+ with open('package.json') as f: pkg = json.load(f)
77
+ except: sys.exit(0)
78
+ for section in ('dependencies', 'devDependencies', 'peerDependencies'):
79
+ for k in pkg.get(section, {}):
80
+ print(k)
81
+ " 2>/dev/null)
82
+ if [ -z "$js_deps" ]; then
83
+ js_deps=$(awk '
84
+ /"dependencies"/{in_deps=1;next}
85
+ /"devDependencies"/{in_deps=1;next}
86
+ /^\s*"[a-zA-Z@]/{
87
+ if(in_deps){
88
+ gsub(/"/,"")
89
+ sub(/:.*/,"")
90
+ if(length>0 && $0 !~ /^dependencies$/ && $0 !~ /^devDependencies$/) print
91
+ }
92
+ }
93
+ /^\s*}/{if(in_deps && depth==0){in_deps=0} else if(in_deps){depth--}}
94
+ /^\s*{/{if(in_deps)depth++}
95
+ ' package.json 2>/dev/null)
96
+ fi
97
+ deps="$deps
98
+ $js_deps"
99
+ fi
100
+
101
+ if [ -f requirements.txt ]; then
102
+ local py_deps
103
+ py_deps=$(grep -vE '^\s*(#|-r|--index-url|--extra-index-url|--find-links)' requirements.txt 2>/dev/null | sed 's/[<>=!].*//' | sed 's/\[.*//')
104
+ deps="$deps
105
+ $py_deps"
106
+ fi
107
+
108
+ if [ -f pyproject.toml ]; then
109
+ local pyproj_deps
110
+ pyproj_deps=$(awk '
111
+ /^\[project\.dependencies\]/{in_deps=1;next}
112
+ /^\[/{in_deps=0}
113
+ in_deps && /^[a-zA-Z]/{
114
+ gsub(/#.*/,"")
115
+ gsub(/;.*$/,"")
116
+ gsub(/"/,"")
117
+ sub(/[<=>!].*/,"")
118
+ sub(/\[.*$/,"")
119
+ if(length>0) print tolower($0)
120
+ }
121
+ ' pyproject.toml 2>/dev/null)
122
+ deps="$deps
123
+ $pyproj_deps"
124
+ fi
125
+
126
+ if [ -f Cargo.toml ]; then
127
+ local cargo_deps
128
+ cargo_deps=$(awk '
129
+ /^\[dependencies\]/{in_deps=1;next}
130
+ /^\[/{in_deps=0}
131
+ in_deps && /^[a-zA-Z0-9_-]/{
132
+ gsub(/#.*/,"")
133
+ sub(/=.*$/,"")
134
+ sub(/\{.*$/,"")
135
+ sub(/".*$/,"")
136
+ if(length>0) print
137
+ }
138
+ ' Cargo.toml 2>/dev/null)
139
+ deps="$deps
140
+ $cargo_deps"
141
+ fi
142
+
143
+ if [ -f go.mod ]; then
144
+ local go_deps
145
+ go_deps=$(awk '
146
+ /^require \(/{in_deps=1;next}
147
+ /^\)/{in_deps=0}
148
+ in_deps && /^\s+\S+/{
149
+ gsub(/#.*/,"")
150
+ sub(/\/\/.*/,"")
151
+ n=split($0,a,"/")
152
+ print tolower(a[n])
153
+ }
154
+ /^require [^(]/{
155
+ gsub(/#.*/,"")
156
+ sub(/^require /,"")
157
+ sub(/\/\/.*/,"")
158
+ n=split($0,a,"/")
159
+ print tolower(a[n])
160
+ }
161
+ ' go.mod 2>/dev/null)
162
+ deps="$deps
163
+ $go_deps"
164
+ fi
165
+
166
+ EXTERNAL_DEPS=$(echo "$deps" | sed '/^\s*$/d' | tr '\n' '|' | sed 's/|$//')
167
+ }
168
+
169
+ is_external() {
170
+ local pkg="$1"
171
+ [ -z "$EXTERNAL_DEPS" ] && return 1
172
+ echo "$EXTERNAL_DEPS" | grep -qiE "(^|\|)${pkg}(\||$)"
173
+ }
174
+
175
+ GO_STDLIB='archive,tar,zip,bufio,builtin,bytes,compress,bzip2,flate,gzip,lzo,zlib,container,heap,list,ring,context,crypto,aes,cipher,des,dsa,ecdsa,ed25519,elliptic,hmac,md5,rand,rc4,rsa,sha256,sha512,subtle,tls,x509,pkix,database,sql,driver,debug,dwarf,elf,macho,pe,plan9obj,buildinfo,dwarf,runtime,encoding,ascii85,asn1,binary,csv,hex,json,jose,gob,base32,base64,pem,xml,errors,expvar,flag,fmt,go,ast,build,constant,doc,format,importer,parser,printer,scanner,token,typecheck,hash,adler32,crc32,crc64,fnv,maphash,html,template,image,color,draw,gif,jpeg,png,font,ccitt,dxt5,gaussian,riff,index,suffixarray,io,ioutil,log,syslog,maps,math,big,cmplx,rand,mime,multipart,quotedprintable,net,http,cgi,cookiejar,fcgi,httputil,pprof,trace,mail,rpc,jsonrpc,smtp,url,text,scanner,tabwriter,template,time,unicode,utf16,utf8,unsafe,os,exec,signal,user,filepath,syscall,unattended,path,reflect,regexp,syntax,runtime,cgo,debug,msan,pprof,race,trace,search,slices,sort,strconv,strings,sync,atomic,map,testing,fstest,iotest,mock,quick,internal'
176
+
177
+ go_is_stdlib() {
178
+ local pkg="$1"
179
+ echo "$GO_STDLIB" | grep -qE "(^|,)"$pkg"(,|$)"
180
+ }
181
+
182
+ extract_and_resolve() {
183
+ local src_module="$1"
184
+ local filepath="$2"
185
+ local ext="${filepath##*.}"
186
+
187
+ case "$ext" in
188
+ js|ts|jsx|tsx|mjs|cjs)
189
+ grep -nE '^\s*(import\s+.*\s+from|import\s+"|import\s+'"'"'|require\s*\()' "$filepath" 2>/dev/null | while IFS= read -r line; do
190
+ imp=$(echo "$line" | sed 's/^[0-9]*://' | sed 's/^\s*//')
191
+ local raw_target=""
192
+ if echo "$imp" | grep -qE 'from\s+["'"'"']'; then
193
+ raw_target=$(echo "$imp" | sed -n "s/.*from\s*[\"']//p" | sed "s/[\"'].*//")
194
+ elif echo "$imp" | grep -qE 'import\s+["'"'"']'; then
195
+ raw_target=$(echo "$imp" | sed -n "s/.*import\s*[\"']//p" | sed "s/[\"'].*//")
196
+ elif echo "$imp" | grep -qE 'require\s*\('; then
197
+ raw_target=$(echo "$imp" | sed -n "s/.*require\s*(\s*[\"']//p" | sed "s/[\"'].*//")
198
+ fi
199
+ [ -z "$raw_target" ] && continue
200
+ if echo "$raw_target" | grep -qE '^\.{1,2}/'; then
201
+ local resolved
202
+ resolved=$(echo "$raw_target" | sed 's|^\./||;s|^\.\./||;s|/index$||;s|\.[^.]*$||')
203
+ local topdir
204
+ topdir=$(echo "$resolved" | cut -d/ -f1)
205
+ if is_internal_dir "$topdir"; then
206
+ printf '%s\t%s\n' "$src_module" "$topdir"
207
+ elif [ -n "$resolved" ]; then
208
+ printf '%s\t%s\n' "$src_module" "$(echo "$resolved" | cut -d/ -f1)"
209
+ fi
210
+ else
211
+ local top_pkg
212
+ top_pkg=$(echo "$raw_target" | sed 's|/.*||;s|@.*/||' | sed 's/@$//')
213
+ if [ -n "$top_pkg" ]; then
214
+ if is_internal_dir "$top_pkg"; then
215
+ printf '%s\t%s\n' "$src_module" "$top_pkg"
216
+ elif is_external "$top_pkg"; then
217
+ printf '%s\t%s\n' "$src_module" "ext:$top_pkg"
218
+ fi
219
+ fi
220
+ fi
221
+ done
222
+ ;;
223
+
224
+ py)
225
+ grep -nE '^\s*(import\s+[a-zA-Z_]|from\s+[\.a-zA-Z_])' "$filepath" 2>/dev/null | while IFS= read -r line; do
226
+ imp=$(echo "$line" | sed 's/^[0-9]*://' | sed 's/^\s*//')
227
+ if echo "$imp" | grep -qE '^\s*import\s+'; then
228
+ targets=$(echo "$imp" | sed 's/^\s*import\s*//' | sed 's/\s*as\s.*//' | sed 's/,/,/g')
229
+ IFS=',' read -ra parts <<< "$targets"
230
+ for part in "${parts[@]}"; do
231
+ pkg=$(echo "$part" | sed 's/^\s*//;s/\s*$//' | cut -d'.' -f1)
232
+ [ -z "$pkg" ] && continue
233
+ if is_internal_dir "$pkg"; then
234
+ printf '%s\t%s\n' "$src_module" "$pkg"
235
+ elif is_external "$pkg"; then
236
+ printf '%s\t%s\n' "$src_module" "ext:$(echo "$pkg" | tr '[:upper:]' '[:lower:]')"
237
+ fi
238
+ done
239
+ unset IFS
240
+ elif echo "$imp" | grep -qE '^\s*from\s+\.'; then
241
+ dots=$(echo "$imp" | sed 's/^\s*from\s*//' | sed 's/[^.].*//' | tr -d '\n' | wc -c)
242
+ afterdots=$(echo "$imp" | sed 's/^\s*from\s*//' | sed 's/^\.*/ /' | sed 's/^\s*//' | awk '{print $1}')
243
+ if [ -z "$afterdots" ] || [ "$afterdots" = "import" ]; then
244
+ :
245
+ else
246
+ local top_pkg
247
+ top_pkg=$(echo "$afterdots" | cut -d'.' -f1)
248
+ if is_internal_dir "$top_pkg"; then
249
+ printf '%s\t%s\n' "$src_module" "$top_pkg"
250
+ fi
251
+ fi
252
+ elif echo "$imp" | grep -qE '^\s*from\s+[a-zA-Z_]'; then
253
+ from_pkg=$(echo "$imp" | sed 's/^\s*from\s*//' | awk '{print $1}')
254
+ top_pkg=$(echo "$from_pkg" | cut -d'.' -f1)
255
+ [ -z "$top_pkg" ] && continue
256
+ if is_internal_dir "$top_pkg"; then
257
+ printf '%s\t%s\n' "$src_module" "$top_pkg"
258
+ elif is_external "$top_pkg"; then
259
+ printf '%s\t%s\n' "$src_module" "ext:$(echo "$top_pkg" | tr '[:upper:]' '[:lower:]')"
260
+ fi
261
+ fi
262
+ done
263
+ ;;
264
+
265
+ go)
266
+ local in_block=0
267
+ grep -nE '^\s*(import\s+"|import\s+\(|use\s+|mod\s+)' "$filepath" 2>/dev/null | while IFS= read -r line; do
268
+ imp=$(echo "$line" | sed 's/^[0-9]*://' | sed 's/^\s*//')
269
+ if echo "$imp" | grep -qE '^\s*import\s+\('; then
270
+ in_block=1
271
+ continue
272
+ fi
273
+ if [ "$in_block" -eq 1 ] && echo "$imp" | grep -qE '^\s*\)'; then
274
+ in_block=0
275
+ continue
276
+ fi
277
+ if [ "$in_block" -eq 1 ]; then
278
+ raw_target=$(echo "$imp" | sed -n 's/.*"\([^"]*\)".*/\1/p')
279
+ elif echo "$imp" | grep -qE '^\s*import\s+"'; then
280
+ raw_target=$(echo "$imp" | sed -n 's/.*import\s*"\([^"]*\)".*/\1/p')
281
+ else
282
+ continue
283
+ fi
284
+ [ -z "$raw_target" ] && continue
285
+ local pkg_base
286
+ pkg_base=$(echo "$raw_target" | awk -F/ '{print $NF}')
287
+ if echo "$raw_target" | grep -qE '^(\.|\.\.|./)'; then
288
+ printf '%s\t%s\n' "$src_module" "$pkg_base"
289
+ elif go_is_stdlib "$pkg_base"; then
290
+ continue
291
+ elif is_external "$pkg_base"; then
292
+ printf '%s\text:%s\n' "$src_module" "$pkg_base"
293
+ elif is_internal_dir "$pkg_base"; then
294
+ printf '%s\t%s\n' "$src_module" "$pkg_base"
295
+ fi
296
+ done
297
+ # Also catch multi-line import blocks that span across grep lines
298
+ awk -v src="$src_module" -v stdlib="$GO_STDLIB" '
299
+ BEGIN { in_block=0 }
300
+ /import[[:space:]]*\(/ { in_block=1; next }
301
+ in_block && /\)/ { in_block=0; next }
302
+ in_block && /"/ {
303
+ match($0, /"[^"]*"/)
304
+ imp = substr($0, RSTART+1, RLENGTH-2)
305
+ if (imp != "") {
306
+ n = split(imp, parts, "/")
307
+ pkg = parts[n]
308
+ is_std = 0
309
+ n2 = split(stdlib, a, ",")
310
+ for (i=1; i<=n2; i++) if (a[i] == pkg) { is_std=1; break }
311
+ if (!is_std) print src "\text:" pkg
312
+ }
313
+ }
314
+ /^import[[:space:]]+"[^"]*"/ {
315
+ match($0, /"[^"]*"/)
316
+ imp = substr($0, RSTART+1, RLENGTH-2)
317
+ if (imp != "") {
318
+ n = split(imp, parts, "/")
319
+ pkg = parts[n]
320
+ is_std = 0
321
+ n2 = split(stdlib, a, ",")
322
+ for (i=1; i<=n2; i++) if (a[i] == pkg) { is_std=1; break }
323
+ if (!is_std) print src "\text:" pkg
324
+ }
325
+ }
326
+ ' "$filepath" 2>/dev/null
327
+ ;;
328
+
329
+ rs)
330
+ grep -nE '^\s*(use\s+|mod\s+)' "$filepath" 2>/dev/null | while IFS= read -r line; do
331
+ imp=$(echo "$line" | sed 's/^[0-9]*://' | sed 's/^\s*//')
332
+ if echo "$imp" | grep -qE '^\s*use\s+crate::'; then
333
+ path=$(echo "$imp" | sed 's/^\s*use\s*crate:://' | sed 's/[{;].*//' | sed 's/\s*$//' | sed 's/::.*//')
334
+ [ -z "$path" ] && continue
335
+ if is_internal_dir "$path"; then
336
+ printf '%s\t%s\n' "$src_module" "$path"
337
+ fi
338
+ elif echo "$imp" | grep -qE '^\s*use\s+super::'; then
339
+ : # skip super references in generic mode
340
+ elif echo "$imp" | grep -qE '^\s*use\s+[a-z]'; then
341
+ crate_name=$(echo "$imp" | sed 's/^\s*use\s*//' | sed 's/[{;].*//' | sed 's/\s*$//' | sed 's/::.*//')
342
+ [ -z "$crate_name" ] && continue
343
+ [ "$crate_name" = "crate" ] || [ "$crate_name" = "super" ] || [ "$crate_name" = "self" ] && continue
344
+ if is_external "$crate_name"; then
345
+ printf '%s\text:%s\n' "$src_module" "$crate_name"
346
+ fi
347
+ elif echo "$imp" | grep -qE '^\s*mod\s+'; then
348
+ mod_name=$(echo "$imp" | sed 's/^\s*mod\s*//' | sed 's/[{;].*//' | sed 's/\s*$//')
349
+ [ -z "$mod_name" ] && continue
350
+ if is_internal_dir "$mod_name"; then
351
+ printf '%s\t%s\n' "$src_module" "$mod_name"
352
+ fi
353
+ fi
354
+ done
355
+ ;;
356
+
357
+ c|h|cpp|cc|cxx|hpp)
358
+ grep -nE '^\s*#\s*include\s*"' "$filepath" 2>/dev/null | while IFS= read -r line; do
359
+ raw_target=$(echo "$line" | sed -n 's/.*#include\s*"\([^"]*\)".*/\1/p')
360
+ [ -z "$raw_target" ] && continue
361
+ local topdir
362
+ topdir=$(echo "$raw_target" | cut -d/ -f1)
363
+ if [ "$topdir" != "$raw_target" ] && is_internal_dir "$topdir"; then
364
+ printf '%s\t%s\n' "$src_module" "$topdir"
365
+ else
366
+ local base
367
+ base=$(echo "$raw_target" | sed 's/\.[^.]*$//')
368
+ if is_internal_dir "$base"; then
369
+ printf '%s\t%s\n' "$src_module" "$base"
370
+ fi
371
+ fi
372
+ done
373
+ ;;
374
+
375
+ java)
376
+ grep -nE '^\s*import\s+' "$filepath" 2>/dev/null | while IFS= read -r line; do
377
+ raw_target=$(echo "$line" | sed 's/.*import\s*//' | sed 's/;.*//' | sed 's/static\s*//')
378
+ [ -z "$raw_target" ] && continue
379
+ local top_pkg
380
+ top_pkg=$(echo "$raw_target" | cut -d'.' -f1)
381
+ if is_internal_dir "$top_pkg"; then
382
+ printf '%s\t%s\n' "$src_module" "$top_pkg"
383
+ elif is_external "$top_pkg"; then
384
+ printf '%s\text:%s\n' "$src_module" "$(echo "$top_pkg" | tr '[:upper:]' '[:lower:]')"
385
+ fi
386
+ done
387
+ ;;
388
+
389
+ rb)
390
+ grep -nE '^\s*(require|require_relative|gem)\s' "$filepath" 2>/dev/null | while IFS= read -r line; do
391
+ imp=$(echo "$line" | sed 's/^[0-9]*://' | sed 's/^\s*//')
392
+ if echo "$imp" | grep -qE 'require_relative'; then
393
+ raw_target=$(echo "$imp" | sed "s/.*require_relative\s*//" | sed "s/['\"].*//" | sed 's/^\s*//;s/\s*$//')
394
+ [ -z "$raw_target" ] && continue
395
+ local topdir
396
+ topdir=$(echo "$raw_target" | cut -d/ -f1)
397
+ if is_internal_dir "$topdir"; then
398
+ printf '%s\t%s\n' "$src_module" "$topdir"
399
+ fi
400
+ elif echo "$imp" | grep -qE '^(\s*)require'; then
401
+ raw_target=$(echo "$imp" | sed "s/.*require\s*//" | sed "s/['\"].*//" | sed 's/^\s*//;s/\s*$//')
402
+ [ -z "$raw_target" ] && continue
403
+ local top_pkg
404
+ top_pkg=$(echo "$raw_target" | cut -d'/' -f1 | tr '[:upper:]' '[:lower:]')
405
+ if is_internal_dir "$top_pkg"; then
406
+ printf '%s\t%s\n' "$src_module" "$top_pkg"
407
+ elif is_external "$top_pkg"; then
408
+ printf '%s\text:%s\n' "$src_module" "$top_pkg"
409
+ fi
410
+ fi
411
+ done
412
+ ;;
413
+
414
+ php)
415
+ grep -nE '^\s*(use\s+|require_once|include_once|require|include)\s' "$filepath" 2>/dev/null | while IFS= read -r line; do
416
+ imp=$(echo "$line" | sed 's/^[0-9]*://' | sed 's/^\s*//')
417
+ if echo "$imp" | grep -qE '^\s*use\s+'; then
418
+ raw_target=$(echo "$imp" | sed 's/.*use\s*//' | sed 's/;.*//' | sed 's/\s*as\s.*//' | cut -d'\\' -f1)
419
+ [ -z "$raw_target" ] && continue
420
+ local top_pkg
421
+ top_pkg=$(echo "$raw_target" | cut -d'\\' -f1 | tr '[:upper:]' '[:lower:]')
422
+ if is_internal_dir "$top_pkg"; then
423
+ printf '%s\t%s\n' "$src_module" "$top_pkg"
424
+ elif is_external "$top_pkg"; then
425
+ printf '%s\text:%s\n' "$src_module" "$top_pkg"
426
+ fi
427
+ fi
428
+ done
429
+ ;;
430
+
431
+ cs)
432
+ grep -nE '^\s*using\s+' "$filepath" 2>/dev/null | while IFS= read -r line; do
433
+ raw_target=$(echo "$line" | sed 's/.*using\s*//' | sed 's/;.*//' | sed 's/\s*=.*//')
434
+ [ -z "$raw_target" ] && continue
435
+ local top_pkg
436
+ top_pkg=$(echo "$raw_target" | cut -d'.' -f1)
437
+ if is_internal_dir "$top_pkg"; then
438
+ printf '%s\t%s\n' "$src_module" "$top_pkg"
439
+ elif is_external "$top_pkg"; then
440
+ printf '%s\text:%s\n' "$src_module" "$(echo "$top_pkg" | tr '[:upper:]' '[:lower:]')"
441
+ fi
442
+ done
443
+ ;;
444
+
445
+ swift|kt)
446
+ grep -nE '^\s*import\s+' "$filepath" 2>/dev/null | while IFS= read -r line; do
447
+ raw_target=$(echo "$line" | sed 's/.*import\s*//' | awk '{print $1}')
448
+ [ -z "$raw_target" ] && continue
449
+ if is_internal_dir "$raw_target"; then
450
+ printf '%s\t%s\n' "$src_module" "$raw_target"
451
+ elif is_external "$raw_target"; then
452
+ printf '%s\text:%s\n' "$src_module" "$(echo "$raw_target" | tr '[:upper:]' '[:lower:]')"
453
+ fi
454
+ done
455
+ ;;
456
+
457
+ scala)
458
+ grep -nE '^\s*import\s+' "$filepath" 2>/dev/null | while IFS= read -r line; do
459
+ raw_target=$(echo "$line" | sed 's/.*import\s*//' | sed 's/\{.*//')
460
+ [ -z "$raw_target" ] && continue
461
+ local top_pkg
462
+ top_pkg=$(echo "$raw_target" | cut -d'.' -f1)
463
+ if is_internal_dir "$top_pkg"; then
464
+ printf '%s\t%s\n' "$src_module" "$top_pkg"
465
+ elif is_external "$top_pkg"; then
466
+ printf '%s\text:%s\n' "$src_module" "$(echo "$top_pkg" | tr '[:upper:]' '[:lower:]')"
467
+ fi
468
+ done
469
+ ;;
470
+
471
+ sh)
472
+ grep -nE '^\s*(source\s+|\.\s+)' "$filepath" 2>/dev/null | while IFS= read -r line; do
473
+ raw_target=$(echo "$line" | sed 's/^[0-9]*://' | sed 's/^\s*//' | sed -e 's/^source\s*//' -e 's/^\.\s*//')
474
+ raw_target=$(echo "$raw_target" | sed 's/^\s*//;s/\s*$//' | sed 's/ .*//')
475
+ [ -z "$raw_target" ] && continue
476
+ local topdir
477
+ topdir=$(echo "$raw_target" | cut -d/ -f1)
478
+ if is_internal_dir "$topdir"; then
479
+ printf '%s\t%s\n' "$src_module" "$topdir"
480
+ fi
481
+ done
482
+ ;;
483
+
484
+ *)
485
+ grep -nE '^\s*(import\s+|require\s*\(|#include\s*"|use\s+|from\s+.*import)' "$filepath" 2>/dev/null | while IFS= read -r line; do
486
+ imp=$(echo "$line" | sed 's/^[0-9]*://' | sed 's/^\s*//')
487
+ local raw_target=""
488
+ if echo "$imp" | grep -qE 'from\s+["'"'"']'; then
489
+ raw_target=$(echo "$imp" | sed -n "s/.*from\s*[\"']//p" | sed "s/[\"'].*//")
490
+ elif echo "$imp" | grep -qE 'import\s+["'"'"']'; then
491
+ raw_target=$(echo "$imp" | sed -n "s/.*import\s*[\"']//p" | sed "s/[\"'].*//")
492
+ elif echo "$imp" | grep -qE 'require\s*\('; then
493
+ raw_target=$(echo "$imp" | sed -n "s/.*require\s*(\s*[\"']//p" | sed "s/[\"'].*//")
494
+ elif echo "$imp" | grep -qE '^\s*import\s+[a-zA-Z_]'; then
495
+ raw_target=$(echo "$imp" | sed 's/.*import\s*//' | awk '{print $1}' | cut -d'.' -f1)
496
+ fi
497
+ [ -z "$raw_target" ] && continue
498
+ if echo "$raw_target" | grep -qE '^\.{1,2}/'; then
499
+ local resolved
500
+ resolved=$(echo "$raw_target" | sed 's|^\./||;s|^\.\./||')
501
+ local topdir
502
+ topdir=$(echo "$resolved" | cut -d/ -f1)
503
+ if is_internal_dir "$topdir"; then
504
+ printf '%s\t%s\n' "$src_module" "$topdir"
505
+ fi
506
+ else
507
+ local top_pkg
508
+ top_pkg=$(echo "$raw_target" | sed 's|/.*||;s|@.*/||' | sed 's/@$//')
509
+ if [ -n "$top_pkg" ]; then
510
+ if is_internal_dir "$top_pkg"; then
511
+ printf '%s\t%s\n' "$src_module" "$top_pkg"
512
+ elif is_external "$top_pkg"; then
513
+ printf '%s\text:%s\n' "$src_module" "$top_pkg"
514
+ fi
515
+ fi
516
+ fi
517
+ done
518
+ ;;
519
+ esac
520
+ }
521
+
522
+ directory_heuristic_edges() {
523
+ local dirs=()
524
+ for entry in "$PROJECT_ROOT"/*; do
525
+ [ -d "$entry" ] || continue
526
+ local dname
527
+ dname=$(basename "$entry")
528
+ echo "$dname" | grep -qE "^($EXCLUDE_DIRS)$" && continue
529
+ echo "$dname" | grep -qE '^\.' && continue
530
+ local has_src
531
+ has_src=$(find "$entry" -maxdepth 2 -type f \( -name "*.js" -o -name "*.ts" -o -name "*.py" -o -name "*.go" -o -name "*.rs" -o -name "*.c" -o -name "*.cpp" -o -name "*.java" -o -name "*.rb" -o -name "*.php" -o -name "*.cs" -o -name "*.swift" -o -name "*.kt" \) -print -quit 2>/dev/null)
532
+ [ -z "$has_src" ] && continue
533
+ dirs+=("$dname")
534
+ done
535
+
536
+ local n=${#dirs[@]}
537
+ if [ "$n" -lt 2 ]; then
538
+ return
539
+ fi
540
+
541
+ for ((i = 0; i < n; i++)); do
542
+ for ((j = i + 1; j < n; j++)); do
543
+ printf '%s\t%s\n' "${dirs[$i]}" "${dirs[$j]}"
544
+ done
545
+ done
546
+ }
547
+
548
+ collect_external_deps
549
+
550
+ (
551
+ has_edges=0
552
+
553
+ while IFS= read -r srcfile; do
554
+ [ -z "$srcfile" ] && continue
555
+
556
+ src_module=$(file_to_module "$srcfile")
557
+ [ -z "$src_module" ] && continue
558
+
559
+ edges=$(extract_and_resolve "$src_module" "$srcfile")
560
+ [ -z "$edges" ] && continue
561
+
562
+ echo "$edges"
563
+ has_edges=1
564
+ done < <(find_source_files)
565
+
566
+ if [ "$has_edges" -eq 0 ]; then
567
+ directory_heuristic_edges
568
+ fi
569
+
570
+ ) 2>/dev/null | sort -u | head -"$((MAX_ARCH * 2))" | MAX_ARCH="$MAX_ARCH" bash "$NORMALIZE"