@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,174 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # Architecture map — Node/TypeScript module dependencies
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
+ if ! command -v python3 &>/dev/null; then
13
+ echo "python3 is required for architecture extraction"
14
+ exit 0
15
+ fi
16
+
17
+ SRC_DIR=""
18
+ for dir in src lib app; do
19
+ [ -d "$dir" ] && SRC_DIR="$dir" && break
20
+ done
21
+ [ -z "$SRC_DIR" ] && { echo "No source directory found"; exit 0; }
22
+
23
+ python3 -c "
24
+ import os, re, json, sys
25
+ from collections import defaultdict
26
+
27
+ SRC_DIR = os.environ.get('SRC_DIR', 'src')
28
+ MAX_FILES = 300
29
+ MAX_MODULES = 40
30
+
31
+ SKIP_DIRS = {'node_modules', 'dist', '.next', 'coverage', 'build', 'out', '.pipemd', '.git', '__tests__', '__test__', '__mocks__', 'test', 'tests', 'spec', 'specs', 'scripts', 'migrations', 'seed', 'public', 'static', 'assets', 'styles', 'views', 'templates'}
32
+
33
+ ENTRY_FILES = {'index', 'main', 'app', 'server', 'mod'}
34
+
35
+ def should_skip_dir(d):
36
+ return d in SKIP_DIRS or d.startswith('.')
37
+
38
+ def should_skip_file(f):
39
+ if f.endswith('.d.ts'):
40
+ return True
41
+ if '.test.' in f or '.spec.' in f or '.stories.' in f or '.story.' in f:
42
+ return True
43
+ return False
44
+
45
+ def module_name(rel_path):
46
+ parts = rel_path.replace(os.sep, '/').split('/')
47
+ filename = os.path.splitext(parts[-1])[0] if parts else ''
48
+ dirpath = parts[:-1] if len(parts) > 1 else []
49
+
50
+ if len(dirpath) == 0:
51
+ return filename if filename in ENTRY_FILES else None
52
+
53
+ last_dir = dirpath[-1]
54
+
55
+ # If there's only one significant file in this dir-level, use the dir name
56
+ # If the file is an entry point (index/main), use the dir name
57
+ if filename in ENTRY_FILES or filename == last_dir:
58
+ return last_dir
59
+
60
+ # Otherwise use dir/file to preserve detail
61
+ return last_dir + '/' + filename
62
+
63
+ def resolve_relative(import_path, file_rel, src_dir):
64
+ file_dir = os.path.dirname(file_rel)
65
+ resolved = os.path.normpath(os.path.join(file_dir, import_path))
66
+ resolved = resolved.replace(os.sep, '/')
67
+ if resolved.startswith(src_dir + '/'):
68
+ resolved = resolved[len(src_dir) + 1:]
69
+ elif resolved == src_dir:
70
+ return None
71
+ return module_name(resolved)
72
+
73
+ def load_external_deps():
74
+ ext = set()
75
+ for pkgfile in ('package.json',):
76
+ try:
77
+ with open(pkgfile, 'r', errors='replace') as f:
78
+ pkg = json.load(f)
79
+ for section in ('dependencies', 'devDependencies', 'peerDependencies'):
80
+ ext.update(pkg.get(section, {}).keys())
81
+ except Exception:
82
+ pass
83
+ return ext
84
+
85
+ def external_name(spec):
86
+ if spec.startswith('@'):
87
+ parts = spec.split('/')
88
+ if len(parts) >= 2:
89
+ return parts[0] + '/' + parts[1]
90
+ return parts[0]
91
+ return spec.split('/')[0]
92
+
93
+ # Match: import X from 'y', import {X} from 'y', require('y'), import('y')
94
+ import_re = re.compile(
95
+ r\"\"\"(?:import\s+(?:.*?)\s+from\s+['\"]([^'\"]+)['\"]|require\s*\(\s*['\"]([^'\"]+)['\"]\s*\)|import\s*\(\s*['\"]([^'\"]+)['\"]\s*\))\"\"\",
96
+ re.MULTILINE
97
+ )
98
+
99
+ ext_deps = load_external_deps()
100
+
101
+ files = []
102
+ for root, dirs, filenames in os.walk(SRC_DIR):
103
+ dirs[:] = sorted([d for d in dirs if not should_skip_dir(d)])
104
+ for fn in sorted(filenames):
105
+ if should_skip_file(fn):
106
+ continue
107
+ if fn.endswith(('.ts', '.tsx', '.js', '.jsx')):
108
+ files.append(os.path.join(root, fn))
109
+ if len(files) >= MAX_FILES:
110
+ files = files[:MAX_FILES]
111
+ break
112
+
113
+ if not files:
114
+ sys.exit(0)
115
+
116
+ # Count files per directory to decide grouping
117
+ dir_counts = defaultdict(list)
118
+ for fpath in files:
119
+ rel = os.path.relpath(fpath, SRC_DIR).replace(os.sep, '/')
120
+ d = os.path.dirname(rel) if '/' in rel else '.'
121
+ dir_counts[d].append(fpath)
122
+
123
+ # Build a set of modules — prefer dir-level for dirs with 1-2 files,
124
+ # file-level for dirs with many files or entry points
125
+ module_set = set()
126
+ for fpath in files:
127
+ rel = os.path.relpath(fpath, SRC_DIR).replace(os.sep, '/')
128
+ mod = module_name(rel)
129
+ if mod:
130
+ module_set.add(mod)
131
+
132
+ # If too many modules, collapse file-level back to dir-level
133
+ if len(module_set) > MAX_MODULES:
134
+ module_set = set()
135
+ for fpath in files:
136
+ rel = os.path.relpath(fpath, SRC_DIR).replace(os.sep, '/')
137
+ parts = rel.split('/')
138
+ if len(parts) > 1:
139
+ module_set.add(parts[0])
140
+ else:
141
+ module_set.add(os.path.splitext(parts[0])[0])
142
+
143
+ edges = set()
144
+
145
+ for fpath in files:
146
+ rel = os.path.relpath(fpath, SRC_DIR).replace(os.sep, '/')
147
+ src_mod = module_name(rel)
148
+ if not src_mod or src_mod not in module_set:
149
+ continue
150
+
151
+ try:
152
+ with open(fpath, 'r', errors='replace') as f:
153
+ content = f.read()
154
+ except Exception:
155
+ continue
156
+
157
+ for m in import_re.finditer(content):
158
+ spec = m.group(1) or m.group(2) or m.group(3)
159
+ if not spec:
160
+ continue
161
+
162
+ if spec.startswith('.'):
163
+ target = resolve_relative(spec, rel, SRC_DIR)
164
+ if target and target in module_set and target != src_mod:
165
+ edges.add((src_mod, target))
166
+ else:
167
+ pkg = external_name(spec)
168
+ if pkg in ext_deps or spec.startswith('@'):
169
+ edges.add((src_mod, 'ext:' + pkg))
170
+
171
+ if edges:
172
+ for s, d in sorted(edges):
173
+ sys.stdout.write(s + '\t' + d + '\n')
174
+ " SRC_DIR="$SRC_DIR" | MAX_ARCH="$MAX_ARCH" bash "$NORMALIZE"
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # Angular route metadata — parses *-routing.module.ts files
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+ files=$(find src -name '*-routing.module.ts' -o -name '*-routing.module.js' 2>/dev/null | grep -v '/node_modules/')
6
+ [ -z "$files" ] && echo "No Angular routing modules found" && exit 0
7
+
8
+ echo "$files" | while IFS= read -r f; do
9
+ [ -z "$f" ] && continue
10
+ echo "File: $f"
11
+ grep -E '(path|redirectTo)\s*:\s*['\''"][^'\''"]*['\''"]' "$f" 2>/dev/null \
12
+ | sed -E 's/(path|redirectTo)\s*:\s*['\''"]([^'\''"]*)['\''"].*/ \1: \2/' \
13
+ | head -"$MAX_ANGULAR"
14
+ echo ""
15
+ done | head -40
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # Next.js App Router route tree — extracts routes from app/**/page.tsx
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+ [ ! -d "app" ] && echo "No Next.js app/ directory found" && exit 0
6
+
7
+ find app -name 'page.tsx' -o -name 'page.ts' -o -name 'page.jsx' -o -name 'page.js' 2>/dev/null \
8
+ | sed 's|^app||;s|/page\.\(tsx\|ts\|jsx\|js\)$||;s|^$|/|' \
9
+ | sort \
10
+ | head -"$MAX_NEXTJS"
11
+
12
+ layouts=$(find app -name 'layout.tsx' -o -name 'layout.ts' 2>/dev/null | wc -l)
13
+ echo "(${layouts} layout(s), $(find app -name 'page.*' 2>/dev/null | wc -l) route(s))"
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # React component metadata — exported function components and Props types
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+ out=$(grep -rn --include='*.tsx' --include='*.jsx' \
6
+ -E '^export (default )?function [A-Z][A-Za-z]*|^export const [A-Z][A-Za-z]* =|^const [A-Z][A-Za-z]*: React\.FC' \
7
+ src/ 2>/dev/null | grep -v 'node_modules' \
8
+ | sed -E 's/:[0-9]+:.*export (default )?(function |const )([A-Z][A-Za-z]*).*/\3/' \
9
+ | sort -u \
10
+ | head -"$MAX_REACT")
11
+ [ -z "$out" ] && echo "No React components found" && exit 0
12
+ echo "$out"
13
+
14
+ props=$(grep -rn --include='*.tsx' --include='*.jsx' \
15
+ -E '(type|interface) [A-Z][A-Za-z]*Props' \
16
+ src/ 2>/dev/null | grep -v 'node_modules' \
17
+ | sed -E 's/.*:(type|interface) ([A-Z][A-Za-z]*Props).*/\2/' \
18
+ | sort -u \
19
+ | head -10)
20
+ [ -n "$props" ] && echo "" && echo "Props types:" && echo "$props"
@@ -0,0 +1,146 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # limit.sh — Smart output limiter with compact fallbacks
4
+ # TOKEN profile multiplier (override via PMD_TOKEN_PROFILE env var)
5
+ PMD_TOKEN_PROFILE="${PMD_TOKEN_PROFILE:-medium}"
6
+ case "$PMD_TOKEN_PROFILE" in
7
+ low) MULT_NUM=1; MULT_DEN=2 ;;
8
+ medium) MULT_NUM=1; MULT_DEN=1 ;;
9
+ high) MULT_NUM=3; MULT_DEN=2 ;;
10
+ xhigh) MULT_NUM=2; MULT_DEN=1 ;;
11
+ unlimited) PMD_UNLIMITED=1 ;;
12
+ *) MULT_NUM=1; MULT_DEN=1 ;;
13
+ esac
14
+
15
+ # Token budget constants (override via PMD_MAX_* env vars, scaled by profile)
16
+ if [ "${PMD_UNLIMITED:-0}" = "1" ]; then
17
+ MAX_TREE=${PMD_MAX_TREE:-99999}
18
+ MAX_DEPS=${PMD_MAX_DEPS:-99999}
19
+ MAX_TODOS=${PMD_MAX_TODOS:-99999}
20
+ MAX_LOG=${PMD_MAX_LOG:-99999}
21
+ MAX_BRANCH=${PMD_MAX_BRANCH:-99999}
22
+ MAX_STATUS=${PMD_MAX_STATUS:-99999}
23
+ MAX_DIFF=${PMD_MAX_DIFF:-99999}
24
+ MAX_TYPECHECK=${PMD_MAX_TYPECHECK:-99999}
25
+ MAX_LINT=${PMD_MAX_LINT:-99999}
26
+ MAX_TEST=${PMD_MAX_TEST:-99999}
27
+ MAX_PRISMA=${PMD_MAX_PRISMA:-99999}
28
+ MAX_EXPRESS=${PMD_MAX_EXPRESS:-99999}
29
+ MAX_FASTAPI=${PMD_MAX_FASTAPI:-99999}
30
+ MAX_DJANGO=${PMD_MAX_DJANGO:-99999}
31
+ MAX_SQLALCHEMY=${PMD_MAX_SQLALCHEMY:-99999}
32
+ MAX_NEST=${PMD_MAX_NEST:-99999}
33
+ MAX_NEXTJS=${PMD_MAX_NEXTJS:-99999}
34
+ MAX_REACT=${PMD_MAX_REACT:-99999}
35
+ MAX_ANGULAR=${PMD_MAX_ANGULAR:-99999}
36
+ MAX_CMAKE=${PMD_MAX_CMAKE:-99999}
37
+ MAX_CLASS=${PMD_MAX_CLASS:-99999}
38
+ MAX_INTERFACE=${PMD_MAX_INTERFACE:-99999}
39
+ MAX_INCLUDE=${PMD_MAX_INCLUDE:-99999}
40
+ MAX_CARGO=${PMD_MAX_CARGO:-99999}
41
+ MAX_GO_PKGS=${PMD_MAX_GO_PKGS:-99999}
42
+ MAX_CARGO_FEATURES=${PMD_MAX_CARGO_FEATURES:-99999}
43
+ MAX_GO_INTERFACES=${PMD_MAX_GO_INTERFACES:-99999}
44
+ MAX_DOCKER=${PMD_MAX_DOCKER:-99999}
45
+ MAX_K8S=${PMD_MAX_K8S:-99999}
46
+ MAX_TF=${PMD_MAX_TF:-99999}
47
+ MAX_AWS=${PMD_MAX_AWS:-99999}
48
+ MAX_ARCH=${PMD_MAX_ARCH:-99999}
49
+ MAX_COMPOSE=${PMD_MAX_COMPOSE:-99999}
50
+ MAX_CREW=${PMD_MAX_CREW:-99999}
51
+ else
52
+ MAX_TREE=$(( (${PMD_MAX_TREE:-50} * MULT_NUM) / MULT_DEN ))
53
+ MAX_DEPS=$(( (${PMD_MAX_DEPS:-40} * MULT_NUM) / MULT_DEN ))
54
+ MAX_TODOS=$(( (${PMD_MAX_TODOS:-20} * MULT_NUM) / MULT_DEN ))
55
+ MAX_LOG=$(( (${PMD_MAX_LOG:-20} * MULT_NUM) / MULT_DEN ))
56
+ MAX_BRANCH=$(( (${PMD_MAX_BRANCH:-20} * MULT_NUM) / MULT_DEN ))
57
+ MAX_STATUS=$(( (${PMD_MAX_STATUS:-30} * MULT_NUM) / MULT_DEN ))
58
+ MAX_DIFF=$(( (${PMD_MAX_DIFF:-30} * MULT_NUM) / MULT_DEN ))
59
+ MAX_TYPECHECK=$(( (${PMD_MAX_TYPECHECK:-30} * MULT_NUM) / MULT_DEN ))
60
+ MAX_LINT=$(( (${PMD_MAX_LINT:-20} * MULT_NUM) / MULT_DEN ))
61
+ MAX_TEST=$(( (${PMD_MAX_TEST:-10} * MULT_NUM) / MULT_DEN ))
62
+ MAX_PRISMA=$(( (${PMD_MAX_PRISMA:-40} * MULT_NUM) / MULT_DEN ))
63
+ MAX_EXPRESS=$(( (${PMD_MAX_EXPRESS:-30} * MULT_NUM) / MULT_DEN ))
64
+ MAX_FASTAPI=$(( (${PMD_MAX_FASTAPI:-30} * MULT_NUM) / MULT_DEN ))
65
+ MAX_DJANGO=$(( (${PMD_MAX_DJANGO:-40} * MULT_NUM) / MULT_DEN ))
66
+ MAX_SQLALCHEMY=$(( (${PMD_MAX_SQLALCHEMY:-40} * MULT_NUM) / MULT_DEN ))
67
+ MAX_NEST=$(( (${PMD_MAX_NEST:-30} * MULT_NUM) / MULT_DEN ))
68
+ MAX_NEXTJS=$(( (${PMD_MAX_NEXTJS:-30} * MULT_NUM) / MULT_DEN ))
69
+ MAX_REACT=$(( (${PMD_MAX_REACT:-30} * MULT_NUM) / MULT_DEN ))
70
+ MAX_ANGULAR=$(( (${PMD_MAX_ANGULAR:-30} * MULT_NUM) / MULT_DEN ))
71
+ MAX_CMAKE=$(( (${PMD_MAX_CMAKE:-40} * MULT_NUM) / MULT_DEN ))
72
+ MAX_CLASS=$(( (${PMD_MAX_CLASS:-40} * MULT_NUM) / MULT_DEN ))
73
+ MAX_INTERFACE=$(( (${PMD_MAX_INTERFACE:-30} * MULT_NUM) / MULT_DEN ))
74
+ MAX_INCLUDE=$(( (${PMD_MAX_INCLUDE:-40} * MULT_NUM) / MULT_DEN ))
75
+ MAX_CARGO=$(( (${PMD_MAX_CARGO:-40} * MULT_NUM) / MULT_DEN ))
76
+ MAX_GO_PKGS=$(( (${PMD_MAX_GO_PKGS:-40} * MULT_NUM) / MULT_DEN ))
77
+ MAX_CARGO_FEATURES=$(( (${PMD_MAX_CARGO_FEATURES:-20} * MULT_NUM) / MULT_DEN ))
78
+ MAX_GO_INTERFACES=$(( (${PMD_MAX_GO_INTERFACES:-30} * MULT_NUM) / MULT_DEN ))
79
+ MAX_DOCKER=$(( (${PMD_MAX_DOCKER:-30} * MULT_NUM) / MULT_DEN ))
80
+ MAX_K8S=$(( (${PMD_MAX_K8S:-20} * MULT_NUM) / MULT_DEN ))
81
+ MAX_TF=$(( (${PMD_MAX_TF:-40} * MULT_NUM) / MULT_DEN ))
82
+ MAX_AWS=$(( (${PMD_MAX_AWS:-10} * MULT_NUM) / MULT_DEN ))
83
+ MAX_ARCH=$(( (${PMD_MAX_ARCH:-100} * MULT_NUM) / MULT_DEN ))
84
+ MAX_COMPOSE=$(( (${PMD_MAX_COMPOSE:-150} * MULT_NUM) / MULT_DEN ))
85
+ MAX_CREW=$(( (${PMD_MAX_CREW:-40} * MULT_NUM) / MULT_DEN ))
86
+ fi
87
+
88
+ TREE_EXCLUDES="${PMD_TREE_EXCLUDES:-node_modules|.git|.pipemd|dist|coverage|.next|.turbo|build|out|__pycache__|venv|.venv|*.egg-info}"
89
+
90
+ limit_output() {
91
+ local text="$1"
92
+ local max="${2:-25}"
93
+ local fallback="$3"
94
+ local lines
95
+ lines=$(echo "$text" | wc -l)
96
+ if [ "$lines" -le "$max" ]; then
97
+ echo "$text"
98
+ else
99
+ echo "$fallback"
100
+ fi
101
+ }
102
+
103
+ limit_tree() {
104
+ local max="${1:-$MAX_TREE}"
105
+ local excl="${TREE_EXCLUDES}"
106
+
107
+ if command -v tree &>/dev/null; then
108
+ local out3
109
+ out3=$(tree -L 3 -I "$excl" --dirsfirst 2>/dev/null)
110
+ local lines3=$(echo "$out3" | wc -l)
111
+
112
+ if [ "$lines3" -le "$max" ]; then
113
+ echo "$out3"
114
+ return
115
+ fi
116
+
117
+ local out2
118
+ out2=$(tree -L 2 -I "$excl" --dirsfirst 2>/dev/null)
119
+ local lines2=$(echo "$out2" | wc -l)
120
+
121
+ if [ "$lines2" -le "$max" ]; then
122
+ echo "$out2"
123
+ echo "(${lines3} lines at depth 3, showing depth 2)"
124
+ return
125
+ fi
126
+
127
+ local out1
128
+ out1=$(tree -L 1 -I "$excl" --dirsfirst 2>/dev/null)
129
+ echo "$out1"
130
+ echo "(${lines3} lines at depth 3, showing depth 1)"
131
+ else
132
+ echo "Project structure:"
133
+ find . -maxdepth 3 \
134
+ -not -path '*/node_modules/*' \
135
+ -not -path '*/.git/*' \
136
+ -not -path '*/.pipemd/*' \
137
+ -not -path '*/dist/*' \
138
+ -not -path '*/build/*' \
139
+ -not -path '*/.next/*' \
140
+ -not -path '*/coverage/*' \
141
+ -not -name 'node_modules' \
142
+ -not -name '.git' \
143
+ -not -name '.pipemd' \
144
+ 2>/dev/null | head -"$max" | sort
145
+ fi
146
+ }
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # Direct dependencies (production + dev + peer) — summarize if too many
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+ if [ ! -f package.json ]; then
6
+ echo "No package.json found"
7
+ exit 0
8
+ fi
9
+ out=$(node -e "const p=require('./package.json');const d={...p.dependencies,...p.devDependencies,...p.peerDependencies};if(!Object.keys(d).length){console.log('No dependencies');process.exit()};Object.entries(d).forEach(([k,v])=>console.log(k+' '+v))" 2>/dev/null)
10
+ if [ -z "$out" ]; then
11
+ echo "No dependencies found in package.json"
12
+ exit 0
13
+ fi
14
+ total=$(echo "$out" | wc -l | tr -d '[:space:]')
15
+ limit_output "$out" "$MAX_DEPS" "$(echo "$out" | head -5 && echo "... and $total total dependencies")"
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # Find TODO, FIXME, HACK — TypeScript/JavaScript files
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+ out=$(grep -rn 'TODO\|FIXME\|HACK' --include="*.ts" --include="*.js" --include="*.tsx" --include="*.jsx" . 2>/dev/null | grep -v '/node_modules/' | grep -v '/.git/' | grep -v '/.pipemd/' | grep -v '/dist/')
6
+ limit_output "$out" "$MAX_TODOS" "$(echo "$out" | head -3 && echo "... and $(($(echo "$out" | wc -l) - 3)) more items")"
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # Lint errors — compact format
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+ if compgen -G ".eslintrc.*" &>/dev/null || compgen -G "eslint.config.*" &>/dev/null || npx eslint --version &>/dev/null; then
6
+ out=$(npx eslint . --format=compact 2>&1 | grep -E "^[^W]")
7
+ limit_output "$out" "$MAX_LINT" "$(echo "$out" | head -3 && echo "... and $(echo "$out" | wc -l) more lint issues")"
8
+ else
9
+ echo "No linter configured"
10
+ fi
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # Test summary — pass/fail counts only
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+
6
+ if compgen -G "jest.config.*" &>/dev/null || [ -f jest.config.js ] || [ -f jest.config.ts ]; then
7
+ out=$(npx jest --no-coverage --silent 2>&1 | tail -5)
8
+ if [ -z "$out" ]; then
9
+ echo "jest ran but produced no output"
10
+ else
11
+ echo "$out"
12
+ fi
13
+ exit 0
14
+ elif compgen -G "vitest.config.*" &>/dev/null || [ -f vitest.config.ts ] || [ -f vitest.config.js ]; then
15
+ out=$(npx vitest run --reporter=verbose 2>&1 | tail -5)
16
+ if [ -z "$out" ]; then
17
+ echo "vitest ran but produced no output"
18
+ else
19
+ echo "$out"
20
+ fi
21
+ exit 0
22
+ fi
23
+
24
+ # Fallback: detect test script in package.json
25
+ if [ -f package.json ]; then
26
+ test_cmd=$(node -e "const p=require('./package.json');console.log(p.scripts&&p.scripts['test:unit']?'test:unit':p.scripts&&p.scripts['test']?'test':'')" 2>/dev/null)
27
+ if [ -n "$test_cmd" ]; then
28
+ pkg_mgr="npm"
29
+ [ -f pnpm-lock.yaml ] && pkg_mgr="pnpm"
30
+ [ -f yarn.lock ] && pkg_mgr="yarn"
31
+ out=$($pkg_mgr run $test_cmd 2>&1 | tail -"$MAX_TEST")
32
+ if [ -n "$out" ]; then
33
+ echo "$out"
34
+ exit 0
35
+ fi
36
+ fi
37
+ fi
38
+
39
+ echo "No test runner configured for this ecosystem"
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # TypeScript type check — errors only
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+ out=$(npx tsc --noEmit 2>&1)
6
+ if [ -z "$out" ]; then
7
+ echo "No type errors"
8
+ exit 0
9
+ fi
10
+ limit_output "$out" "$MAX_TYPECHECK" "$(echo "$out" | head -5 && echo "... and $(echo "$out" | grep -c 'error TS') total errors")"
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env bash
2
+ set -uo pipefail
3
+ # FastAPI route metadata — endpoint signatures
4
+ source "$(dirname "$0")/../lib/limit.sh"
5
+ out=$(grep -rn --include='*.py' \
6
+ -E '@(app|router|APIRouter)\.(get|post|put|delete|patch)\(' \
7
+ . 2>/dev/null \
8
+ | grep -v 'venv' | grep -v '.venv' | grep -v '__pycache__' \
9
+ | sed -E 's/.*@(app|router|APIRouter)\.(get|post|put|delete|patch)\(\s*['\''"](\/[^'\''"]*)['\''"].*/\U\2 \3/' \
10
+ | head -"$MAX_FASTAPI")
11
+ [ -z "$out" ] && echo "No FastAPI routes found" && exit 0
12
+ echo "$out"