@soulbatical/tetra-dev-toolkit 1.2.0 → 1.3.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/bin/cleanup-repos.sh +287 -0
- package/bin/tetra-audit.js +5 -1
- package/bin/tetra-setup.js +35 -1
- package/lib/checks/health/file-organization.js +389 -0
- package/lib/checks/health/index.js +1 -0
- package/lib/checks/health/scanner.js +3 -1
- package/lib/checks/health/stella-integration.js +7 -7
- package/lib/checks/health/types.js +1 -1
- package/lib/checks/hygiene/file-organization.js +105 -0
- package/lib/checks/index.js +4 -0
- package/lib/checks/stability/ci-pipeline.js +1 -1
- package/lib/checks/supabase/rpc-param-mismatch.js +453 -0
- package/lib/config.js +2 -1
- package/lib/runner.js +15 -2
- package/package.json +2 -2
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# Repo Cleanup Script — Verplaats alles naar de juiste plek
|
|
4
|
+
#
|
|
5
|
+
# Wat het doet:
|
|
6
|
+
# 1. Stray .md files → /docs/_moved/{oorspronkelijk-pad}/
|
|
7
|
+
# 2. Stray .sh scripts → /scripts/_moved/{oorspronkelijk-pad}/
|
|
8
|
+
# 3. Nested docs/ (frontend/docs/, backend/docs/) → /docs/{subdir-naam}/
|
|
9
|
+
# 4. Root clutter files (.txt, .png, .csv, .py) → /docs/_cleanup/
|
|
10
|
+
# 5. Code dir clutter (.txt, .log, .env) → /docs/_cleanup/{code-dir}/
|
|
11
|
+
# 6. Clutter dirs (tmp/, logs/, data/, etc.) → .gitignore
|
|
12
|
+
# 7. .env/.env.local in code dirs → verwijder (secrets horen niet in git)
|
|
13
|
+
#
|
|
14
|
+
# DRY RUN: standaard toont het alleen wat het zou doen.
|
|
15
|
+
# EXECUTE: ./cleanup-repos.sh --execute
|
|
16
|
+
# =============================================================================
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
|
|
20
|
+
DRY_RUN=true
|
|
21
|
+
[[ "${1:-}" == "--execute" ]] && DRY_RUN=false
|
|
22
|
+
|
|
23
|
+
BASE="$HOME/projecten"
|
|
24
|
+
PROJECTS=(sparkbuddy-live agentrook ralph-manager vincifox snelstart-mcp web-mcp)
|
|
25
|
+
|
|
26
|
+
# Colors
|
|
27
|
+
RED='\033[0;31m'
|
|
28
|
+
GREEN='\033[0;32m'
|
|
29
|
+
YELLOW='\033[1;33m'
|
|
30
|
+
BLUE='\033[0;34m'
|
|
31
|
+
NC='\033[0m'
|
|
32
|
+
|
|
33
|
+
# Counters
|
|
34
|
+
TOTAL_MOVED=0
|
|
35
|
+
TOTAL_GITIGNORED=0
|
|
36
|
+
TOTAL_DELETED=0
|
|
37
|
+
|
|
38
|
+
lower() { echo "$1" | tr '[:upper:]' '[:lower:]'; }
|
|
39
|
+
|
|
40
|
+
log() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
41
|
+
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
42
|
+
action() { echo -e "${GREEN}[MOVE]${NC} $1"; }
|
|
43
|
+
danger() { echo -e "${RED}[DEL]${NC} $1"; }
|
|
44
|
+
|
|
45
|
+
safe_move() {
|
|
46
|
+
local src="$1" dst="$2"
|
|
47
|
+
|
|
48
|
+
if $DRY_RUN; then
|
|
49
|
+
action "$src → $dst"
|
|
50
|
+
else
|
|
51
|
+
mkdir -p "$(dirname "$PROJECT_PATH/$dst")"
|
|
52
|
+
mv "$PROJECT_PATH/$src" "$PROJECT_PATH/$dst"
|
|
53
|
+
fi
|
|
54
|
+
TOTAL_MOVED=$((TOTAL_MOVED + 1))
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
safe_delete() {
|
|
58
|
+
local target="$1"
|
|
59
|
+
if $DRY_RUN; then
|
|
60
|
+
danger "DELETE $target"
|
|
61
|
+
else
|
|
62
|
+
rm -f "$target"
|
|
63
|
+
fi
|
|
64
|
+
TOTAL_DELETED=$((TOTAL_DELETED + 1))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
add_gitignore() {
|
|
68
|
+
local project_path="$1" entry="$2"
|
|
69
|
+
local gitignore="$project_path/.gitignore"
|
|
70
|
+
|
|
71
|
+
if [ -f "$gitignore" ] && grep -qF "$entry" "$gitignore" 2>/dev/null; then
|
|
72
|
+
return
|
|
73
|
+
fi
|
|
74
|
+
|
|
75
|
+
if $DRY_RUN; then
|
|
76
|
+
log "Add to .gitignore: $entry"
|
|
77
|
+
else
|
|
78
|
+
echo "$entry" >> "$gitignore"
|
|
79
|
+
fi
|
|
80
|
+
TOTAL_GITIGNORED=$((TOTAL_GITIGNORED + 1))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
is_allowed_root_md() {
|
|
84
|
+
local name_lc
|
|
85
|
+
name_lc=$(lower "$1")
|
|
86
|
+
case "$name_lc" in
|
|
87
|
+
readme.md|claude.md|changelog.md|license.md|prompt.md|plan.md|contributing.md|code_of_conduct.md)
|
|
88
|
+
return 0 ;;
|
|
89
|
+
*) return 1 ;;
|
|
90
|
+
esac
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
# =============================================================================
|
|
94
|
+
# Main cleanup per project
|
|
95
|
+
# =============================================================================
|
|
96
|
+
|
|
97
|
+
for PROJECT in "${PROJECTS[@]}"; do
|
|
98
|
+
PROJECT_PATH="$BASE/$PROJECT"
|
|
99
|
+
[ -d "$PROJECT_PATH" ] || continue
|
|
100
|
+
|
|
101
|
+
echo ""
|
|
102
|
+
echo "================================================================"
|
|
103
|
+
echo -e "${BLUE}=== $PROJECT ===${NC}"
|
|
104
|
+
echo "================================================================"
|
|
105
|
+
|
|
106
|
+
# --- 1. Nested docs/ → merge into /docs/{subdir-naam}/ (FIRST, before stray .md scan) ---
|
|
107
|
+
log "Checking nested docs/ directories..."
|
|
108
|
+
for subdir in frontend backend backend-mcp backend-pdf mcp shell; do
|
|
109
|
+
nested_docs="$PROJECT_PATH/$subdir/docs"
|
|
110
|
+
[ -d "$nested_docs" ] || continue
|
|
111
|
+
|
|
112
|
+
file_count=$(find "$nested_docs" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
113
|
+
[[ "$file_count" == "0" ]] && continue
|
|
114
|
+
|
|
115
|
+
log "Moving $subdir/docs/ ($file_count files) → docs/$subdir/"
|
|
116
|
+
while IFS= read -r -d '' file; do
|
|
117
|
+
rel_from_nested="${file#$nested_docs/}"
|
|
118
|
+
safe_move "$subdir/docs/$rel_from_nested" "docs/$subdir/$rel_from_nested"
|
|
119
|
+
done < <(find "$nested_docs" -type f -print0 2>/dev/null)
|
|
120
|
+
done
|
|
121
|
+
|
|
122
|
+
# --- 2. Stray .md files → /docs/_moved/ ---
|
|
123
|
+
log "Checking stray .md files..."
|
|
124
|
+
while IFS= read -r -d '' file; do
|
|
125
|
+
rel="${file#$PROJECT_PATH/}"
|
|
126
|
+
name=$(basename "$file")
|
|
127
|
+
name_lc=$(lower "$name")
|
|
128
|
+
top_dir="${rel%%/*}"
|
|
129
|
+
|
|
130
|
+
# Skip README.md and CLAUDE.md anywhere (standard practice)
|
|
131
|
+
case "$name_lc" in
|
|
132
|
+
readme.md|claude.md) continue ;;
|
|
133
|
+
esac
|
|
134
|
+
|
|
135
|
+
# Skip root allowed .md files
|
|
136
|
+
[[ "$rel" == "$name" ]] && is_allowed_root_md "$name" && continue
|
|
137
|
+
|
|
138
|
+
# Skip /docs/, .ralph/, .claude/, .agents/, e2e/
|
|
139
|
+
case "$top_dir" in
|
|
140
|
+
docs|.ralph|.claude|.agents|e2e) continue ;;
|
|
141
|
+
esac
|
|
142
|
+
|
|
143
|
+
# Skip shell/templates/
|
|
144
|
+
[[ "$rel" == shell/templates/* ]] && continue
|
|
145
|
+
|
|
146
|
+
# Skip files already handled by nested docs merge (avoid double-move)
|
|
147
|
+
[[ "$rel" == */docs/* ]] && continue
|
|
148
|
+
|
|
149
|
+
safe_move "$rel" "docs/_moved/$rel"
|
|
150
|
+
done < <(find "$PROJECT_PATH" -maxdepth 5 -name "*.md" -type f \
|
|
151
|
+
-not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/dist/*" \
|
|
152
|
+
-not -path "*/.next/*" -not -path "*/coverage/*" -print0 2>/dev/null)
|
|
153
|
+
|
|
154
|
+
# --- 3. Stray .sh scripts → /scripts/_moved/ ---
|
|
155
|
+
log "Checking stray .sh scripts..."
|
|
156
|
+
while IFS= read -r -d '' file; do
|
|
157
|
+
rel="${file#$PROJECT_PATH/}"
|
|
158
|
+
top_dir="${rel%%/*}"
|
|
159
|
+
|
|
160
|
+
# Skip root-level scripts
|
|
161
|
+
[[ "$rel" != */* ]] && continue
|
|
162
|
+
|
|
163
|
+
# Skip allowed dirs
|
|
164
|
+
case "$top_dir" in
|
|
165
|
+
scripts|shell|hooks|.husky|.ralph|.claude) continue ;;
|
|
166
|
+
esac
|
|
167
|
+
|
|
168
|
+
safe_move "$rel" "scripts/_moved/$rel"
|
|
169
|
+
done < <(find "$PROJECT_PATH" -maxdepth 5 -name "*.sh" -type f \
|
|
170
|
+
-not -path "*/node_modules/*" -not -path "*/.git/*" -not -path "*/dist/*" \
|
|
171
|
+
-print0 2>/dev/null)
|
|
172
|
+
|
|
173
|
+
# --- 4. Root clutter files → /docs/_cleanup/ ---
|
|
174
|
+
log "Checking root clutter..."
|
|
175
|
+
for file in "$PROJECT_PATH"/*; do
|
|
176
|
+
[ -f "$file" ] || continue
|
|
177
|
+
name=$(basename "$file")
|
|
178
|
+
name_lc=$(lower "$name")
|
|
179
|
+
|
|
180
|
+
# Skip dotfiles
|
|
181
|
+
[[ "$name" == .* ]] && continue
|
|
182
|
+
|
|
183
|
+
# Skip known config extensions and types handled by other checks
|
|
184
|
+
case "$name_lc" in
|
|
185
|
+
*.json|*.js|*.cjs|*.mjs|*.ts|*.toml|*.lock) continue ;;
|
|
186
|
+
*.md|*.sh|*.yml|*.yaml) continue ;;
|
|
187
|
+
dockerfile*|procfile) continue ;;
|
|
188
|
+
esac
|
|
189
|
+
|
|
190
|
+
# Check for clutter extensions
|
|
191
|
+
ext=".${name_lc##*.}"
|
|
192
|
+
case "$ext" in
|
|
193
|
+
.txt|.log|.pdf|.csv|.png|.jpg|.jpeg|.gif|.py|.bak|.tmp|.orig|.patch|.diff)
|
|
194
|
+
safe_move "$name" "docs/_cleanup/$name"
|
|
195
|
+
;;
|
|
196
|
+
esac
|
|
197
|
+
done
|
|
198
|
+
|
|
199
|
+
# --- 5. Code dir clutter → /docs/_cleanup/{code-dir}/ ---
|
|
200
|
+
log "Checking code dir clutter..."
|
|
201
|
+
for codeDir in backend frontend backend-mcp backend-pdf mcp; do
|
|
202
|
+
code_path="$PROJECT_PATH/$codeDir"
|
|
203
|
+
[ -d "$code_path" ] || continue
|
|
204
|
+
|
|
205
|
+
while IFS= read -r -d '' file; do
|
|
206
|
+
name=$(basename "$file")
|
|
207
|
+
name_lc=$(lower "$name")
|
|
208
|
+
rel="${file#$PROJECT_PATH/}"
|
|
209
|
+
rel_from_code="${file#$code_path/}"
|
|
210
|
+
sub_top="${rel_from_code%%/*}"
|
|
211
|
+
|
|
212
|
+
# Skip allowed subdirs
|
|
213
|
+
case "$sub_top" in
|
|
214
|
+
public|static|assets|fixtures|supabase|migrations|prisma|test-fixtures) continue ;;
|
|
215
|
+
esac
|
|
216
|
+
|
|
217
|
+
# .env files → DELETE (secrets!)
|
|
218
|
+
case "$name_lc" in
|
|
219
|
+
.env|.env.local|.env.development|.env.production|.env.staging)
|
|
220
|
+
danger "SECRET: $rel"
|
|
221
|
+
safe_delete "$file"
|
|
222
|
+
continue
|
|
223
|
+
;;
|
|
224
|
+
esac
|
|
225
|
+
|
|
226
|
+
# Skip templates
|
|
227
|
+
case "$name_lc" in
|
|
228
|
+
*.example|*.test) continue ;;
|
|
229
|
+
esac
|
|
230
|
+
|
|
231
|
+
# Skip standard web files
|
|
232
|
+
case "$name_lc" in
|
|
233
|
+
robots.txt|llms.txt|llms-full.txt|humans.txt|security.txt|ads.txt) continue ;;
|
|
234
|
+
esac
|
|
235
|
+
|
|
236
|
+
# Check for clutter extensions
|
|
237
|
+
ext=".${name_lc##*.}"
|
|
238
|
+
case "$ext" in
|
|
239
|
+
.txt|.log|.pdf|.csv|.bak|.tmp|.orig|.patch|.diff)
|
|
240
|
+
safe_move "$rel" "docs/_cleanup/$rel"
|
|
241
|
+
;;
|
|
242
|
+
esac
|
|
243
|
+
done < <(find "$code_path" -maxdepth 4 -type f \
|
|
244
|
+
-not -path "*/node_modules/*" -not -path "*/dist/*" -not -path "*/.next/*" \
|
|
245
|
+
-not -path "*/coverage/*" -not -path "*/.turbo/*" -not -path "*/test-results/*" \
|
|
246
|
+
\( -name "*.txt" -o -name "*.log" -o -name "*.pdf" -o -name "*.csv" \
|
|
247
|
+
-o -name "*.bak" -o -name "*.tmp" -o -name "*.orig" \
|
|
248
|
+
-o -name ".env" -o -name ".env.*" \) -print0 2>/dev/null)
|
|
249
|
+
done
|
|
250
|
+
|
|
251
|
+
# --- 6. Clutter dirs → .gitignore ---
|
|
252
|
+
log "Checking clutter directories..."
|
|
253
|
+
for dir_name in tmp temp logs log data venv .venv reports output out backup backups; do
|
|
254
|
+
if [ -d "$PROJECT_PATH/$dir_name" ]; then
|
|
255
|
+
add_gitignore "$PROJECT_PATH" "$dir_name/"
|
|
256
|
+
warn "Directory $dir_name/ → added to .gitignore"
|
|
257
|
+
fi
|
|
258
|
+
done
|
|
259
|
+
|
|
260
|
+
done
|
|
261
|
+
|
|
262
|
+
# =============================================================================
|
|
263
|
+
# Summary
|
|
264
|
+
# =============================================================================
|
|
265
|
+
|
|
266
|
+
echo ""
|
|
267
|
+
echo "================================================================"
|
|
268
|
+
if $DRY_RUN; then
|
|
269
|
+
echo -e "${YELLOW}DRY RUN COMPLETE${NC}"
|
|
270
|
+
echo " Files to move: $TOTAL_MOVED"
|
|
271
|
+
echo " Files to delete: $TOTAL_DELETED"
|
|
272
|
+
echo " Gitignore entries: $TOTAL_GITIGNORED"
|
|
273
|
+
echo ""
|
|
274
|
+
echo "Run with --execute to apply changes:"
|
|
275
|
+
echo " bash cleanup-repos.sh --execute"
|
|
276
|
+
else
|
|
277
|
+
echo -e "${GREEN}CLEANUP COMPLETE${NC}"
|
|
278
|
+
echo " Files moved: $TOTAL_MOVED"
|
|
279
|
+
echo " Files deleted: $TOTAL_DELETED"
|
|
280
|
+
echo " Gitignore entries: $TOTAL_GITIGNORED"
|
|
281
|
+
echo ""
|
|
282
|
+
echo "Next steps:"
|
|
283
|
+
echo " 1. Review /docs/_moved/ and /docs/_cleanup/ per project"
|
|
284
|
+
echo " 2. Delete what you don't need, keep what's useful"
|
|
285
|
+
echo " 3. git add + commit per project"
|
|
286
|
+
fi
|
|
287
|
+
echo "================================================================"
|
package/bin/tetra-audit.js
CHANGED
|
@@ -7,13 +7,14 @@
|
|
|
7
7
|
* tetra-audit # Run all checks
|
|
8
8
|
* tetra-audit security # Run security checks only
|
|
9
9
|
* tetra-audit stability # Run stability checks only
|
|
10
|
+
* tetra-audit hygiene # Run repo hygiene checks only
|
|
10
11
|
* tetra-audit quick # Run quick critical checks
|
|
11
12
|
* tetra-audit --ci # CI mode (GitHub Actions annotations)
|
|
12
13
|
* tetra-audit --json # JSON output
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import { program } from 'commander'
|
|
16
|
-
import { runAllChecks, runSecurityChecks, runStabilityChecks, runCodeQualityChecks, runQuickCheck } from '../lib/runner.js'
|
|
17
|
+
import { runAllChecks, runSecurityChecks, runStabilityChecks, runCodeQualityChecks, runHygieneChecks, runQuickCheck } from '../lib/runner.js'
|
|
17
18
|
import { formatResults, formatGitHubActions } from '../lib/reporters/terminal.js'
|
|
18
19
|
|
|
19
20
|
program
|
|
@@ -40,6 +41,9 @@ program
|
|
|
40
41
|
case 'code-quality':
|
|
41
42
|
results = await runCodeQualityChecks()
|
|
42
43
|
break
|
|
44
|
+
case 'hygiene':
|
|
45
|
+
results = await runHygieneChecks()
|
|
46
|
+
break
|
|
43
47
|
case 'quick':
|
|
44
48
|
results = await runQuickCheck()
|
|
45
49
|
break
|
package/bin/tetra-setup.js
CHANGED
|
@@ -120,6 +120,39 @@ echo "✅ Pre-commit checks passed"
|
|
|
120
120
|
console.log(' ⏭️ .husky/pre-commit already exists (use --force to overwrite)')
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
+
// Create or extend pre-push hook with hygiene check
|
|
124
|
+
const prePushPath = join(huskyDir, 'pre-push')
|
|
125
|
+
const hygieneBlock = `
|
|
126
|
+
# Tetra hygiene check — blocks push if repo contains clutter
|
|
127
|
+
echo "🧹 Running repo hygiene check..."
|
|
128
|
+
npx tetra-audit hygiene
|
|
129
|
+
if [ $? -ne 0 ]; then
|
|
130
|
+
echo ""
|
|
131
|
+
echo "❌ Repo hygiene issues found! Clean up before pushing."
|
|
132
|
+
echo " Run 'npx tetra-audit hygiene --verbose' for details."
|
|
133
|
+
echo " Run 'bash node_modules/@soulbatical/tetra-dev-toolkit/bin/cleanup-repos.sh' to auto-fix."
|
|
134
|
+
exit 1
|
|
135
|
+
fi
|
|
136
|
+
echo "✅ Repo hygiene passed"
|
|
137
|
+
`
|
|
138
|
+
|
|
139
|
+
if (!existsSync(prePushPath)) {
|
|
140
|
+
// No pre-push hook yet — create one
|
|
141
|
+
const prePushContent = `#!/bin/sh\n${hygieneBlock}\n`
|
|
142
|
+
writeFileSync(prePushPath, prePushContent)
|
|
143
|
+
execSync(`chmod +x ${prePushPath}`)
|
|
144
|
+
console.log(' ✅ Created .husky/pre-push with hygiene check')
|
|
145
|
+
} else {
|
|
146
|
+
// Pre-push hook exists — add hygiene check if not already there
|
|
147
|
+
const existing = readFileSync(prePushPath, 'utf-8')
|
|
148
|
+
if (!existing.includes('tetra-audit hygiene')) {
|
|
149
|
+
writeFileSync(prePushPath, existing.trimEnd() + '\n' + hygieneBlock)
|
|
150
|
+
console.log(' ✅ Added hygiene check to existing .husky/pre-push')
|
|
151
|
+
} else {
|
|
152
|
+
console.log(' ⏭️ .husky/pre-push already has hygiene check')
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
123
156
|
// Add prepare script to package.json
|
|
124
157
|
if (!pkg.scripts?.prepare?.includes('husky')) {
|
|
125
158
|
pkg.scripts = pkg.scripts || {}
|
|
@@ -194,7 +227,8 @@ async function setupConfig(options) {
|
|
|
194
227
|
"security": true,
|
|
195
228
|
"stability": true,
|
|
196
229
|
"codeQuality": true,
|
|
197
|
-
"supabase": "auto"
|
|
230
|
+
"supabase": "auto",
|
|
231
|
+
"hygiene": true
|
|
198
232
|
},
|
|
199
233
|
"security": {
|
|
200
234
|
"checkHardcodedSecrets": true,
|