@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.
- package/AI_SETUP_PIPEMD.md +184 -0
- package/CHANGELOG.md +47 -0
- package/LICENSE +15 -0
- package/README.md +535 -0
- package/dist/index.js +6647 -0
- package/dist/plugins/opencode-server.js +235 -0
- package/dist/plugins/opencode-tui.js +914 -0
- package/dist/templates/agent-decision-tree.md +113 -0
- package/dist/templates/static-rules.md +7 -0
- package/package.json +68 -0
- package/scripts/C-CPP/architecture/arch.sh +229 -0
- package/scripts/C-CPP/lib/limit.sh +146 -0
- package/scripts/C-CPP/project/class-diagram.sh +96 -0
- package/scripts/C-CPP/project/cmake-targets.sh +68 -0
- package/scripts/C-CPP/project/deps.sh +44 -0
- package/scripts/C-CPP/project/find-todos.sh +6 -0
- package/scripts/C-CPP/project/include-graph.sh +110 -0
- package/scripts/C-CPP/project/interfaces.sh +108 -0
- package/scripts/C-CPP/project/tree.sh +5 -0
- package/scripts/C-CPP/quality/lint.sh +14 -0
- package/scripts/C-CPP/quality/test-summary.sh +22 -0
- package/scripts/C-CPP/quality/type-check.sh +26 -0
- package/scripts/DevOps/architecture/arch.sh +186 -0
- package/scripts/DevOps/devops/aws-context.sh +34 -0
- package/scripts/DevOps/devops/docker-stats.sh +42 -0
- package/scripts/DevOps/devops/k8s-unhealthy.sh +41 -0
- package/scripts/DevOps/devops/tf-state.sh +65 -0
- package/scripts/DevOps/lib/limit.sh +143 -0
- package/scripts/Generic/architecture/arch.sh +570 -0
- package/scripts/Generic/lib/limit.sh +140 -0
- package/scripts/Go/architecture/arch.sh +79 -0
- package/scripts/Go/lib/limit.sh +142 -0
- package/scripts/Go/project/deps.sh +35 -0
- package/scripts/Go/project/find-todos.sh +6 -0
- package/scripts/Go/project/go-interfaces.sh +18 -0
- package/scripts/Go/project/go-packages.sh +28 -0
- package/scripts/Go/project/tree.sh +5 -0
- package/scripts/Go/quality/lint.sh +16 -0
- package/scripts/Go/quality/test-summary.sh +16 -0
- package/scripts/Go/quality/type-check.sh +16 -0
- package/scripts/Node-TypeScript/api/express-routes.sh +14 -0
- package/scripts/Node-TypeScript/api/nest-controllers.sh +18 -0
- package/scripts/Node-TypeScript/architecture/arch.sh +174 -0
- package/scripts/Node-TypeScript/frontend/angular-routes.sh +15 -0
- package/scripts/Node-TypeScript/frontend/nextjs-app-router.sh +13 -0
- package/scripts/Node-TypeScript/frontend/react-components.sh +20 -0
- package/scripts/Node-TypeScript/lib/limit.sh +146 -0
- package/scripts/Node-TypeScript/project/deps.sh +15 -0
- package/scripts/Node-TypeScript/project/find-todos.sh +6 -0
- package/scripts/Node-TypeScript/quality/lint.sh +10 -0
- package/scripts/Node-TypeScript/quality/test-summary.sh +39 -0
- package/scripts/Node-TypeScript/quality/type-check.sh +10 -0
- package/scripts/Python/api/fastapi-routes.sh +12 -0
- package/scripts/Python/architecture/arch.sh +220 -0
- package/scripts/Python/db/django-models.sh +12 -0
- package/scripts/Python/db/sqlalchemy.sh +17 -0
- package/scripts/Python/lib/limit.sh +144 -0
- package/scripts/Python/project/deps.sh +28 -0
- package/scripts/Python/project/find-todos.sh +6 -0
- package/scripts/Python/quality/lint.sh +13 -0
- package/scripts/Python/quality/test-summary.sh +11 -0
- package/scripts/Python/quality/type-check.sh +10 -0
- package/scripts/Rust/architecture/arch.sh +176 -0
- package/scripts/Rust/lib/limit.sh +142 -0
- package/scripts/Rust/project/cargo-deps.sh +42 -0
- package/scripts/Rust/project/cargo-features.sh +26 -0
- package/scripts/Rust/project/find-todos.sh +6 -0
- package/scripts/Rust/project/tree.sh +5 -0
- package/scripts/Rust/quality/lint.sh +16 -0
- package/scripts/Rust/quality/test-summary.sh +16 -0
- package/scripts/Rust/quality/type-check.sh +16 -0
- package/scripts/Shared/api/express-routes.sh +11 -0
- package/scripts/Shared/api/fastapi-routes.sh +10 -0
- package/scripts/Shared/api/nest-controllers.sh +22 -0
- package/scripts/Shared/architecture/normalize.sh +178 -0
- package/scripts/Shared/crew/crew.sh +15 -0
- package/scripts/Shared/db/django-models.sh +11 -0
- package/scripts/Shared/db/prisma.sh +33 -0
- package/scripts/Shared/db/sqlalchemy.sh +12 -0
- package/scripts/Shared/frontend/angular-routes.sh +11 -0
- package/scripts/Shared/frontend/nextjs-app-router.sh +13 -0
- package/scripts/Shared/frontend/react-components.sh +11 -0
- package/scripts/Shared/git/diff-stat.sh +6 -0
- package/scripts/Shared/git/git-branch.sh +16 -0
- package/scripts/Shared/git/git-log.sh +6 -0
- package/scripts/Shared/git/git-status.sh +6 -0
- package/scripts/Shared/lib/limit.sh +144 -0
- package/scripts/Shared/project/compose-md.sh +182 -0
- package/scripts/Shared/project/deps.sh +69 -0
- package/scripts/Shared/project/find-todos.sh +6 -0
- package/scripts/Shared/project/tree.sh +5 -0
- package/scripts/Shared/quality/lint.sh +81 -0
- package/scripts/Shared/quality/test-summary.sh +103 -0
- package/scripts/Shared/quality/type-check.sh +114 -0
- package/scripts/copy-plugins.mjs +4 -0
- 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"
|