@soulbatical/tetra-dev-toolkit 1.2.0 → 1.3.1
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/README.md +198 -0
- 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 +3 -1
- package/lib/checks/health/rpc-param-mismatch.js +92 -0
- package/lib/checks/health/scanner.js +6 -2
- package/lib/checks/health/stella-integration.js +7 -7
- package/lib/checks/health/types.js +2 -2
- 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
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# @soulbatical/tetra-dev-toolkit
|
|
2
|
+
|
|
3
|
+
Quality, security, and hygiene checks for all Soulbatical projects.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install --save-dev @soulbatical/tetra-dev-toolkit
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup (A-Z)
|
|
12
|
+
|
|
13
|
+
One command installs everything — hooks, CI, config:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx tetra-setup
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
This creates:
|
|
20
|
+
- `.husky/pre-commit` — quick security checks before every commit
|
|
21
|
+
- `.husky/pre-push` — hygiene check before every push (blocks clutter)
|
|
22
|
+
- `.github/workflows/quality.yml` — full audit on PR/push to main
|
|
23
|
+
- `.tetra-quality.json` — project config (override defaults)
|
|
24
|
+
|
|
25
|
+
To install individual components:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx tetra-setup hooks # Husky hooks only
|
|
29
|
+
npx tetra-setup ci # GitHub Actions only
|
|
30
|
+
npx tetra-setup config # Config file only
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Re-running `tetra-setup hooks` on an existing project adds missing hooks without overwriting existing ones.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
npx tetra-audit # Run all checks
|
|
39
|
+
npx tetra-audit security # Security checks only
|
|
40
|
+
npx tetra-audit stability # Stability checks only
|
|
41
|
+
npx tetra-audit codeQuality # Code quality checks only
|
|
42
|
+
npx tetra-audit supabase # Supabase checks only
|
|
43
|
+
npx tetra-audit hygiene # Repo hygiene checks only
|
|
44
|
+
npx tetra-audit quick # Quick critical checks (pre-commit)
|
|
45
|
+
npx tetra-audit --ci # CI mode (GitHub Actions annotations)
|
|
46
|
+
npx tetra-audit --json # JSON output
|
|
47
|
+
npx tetra-audit --verbose # Detailed output with fix suggestions
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Exit codes: `0` = passed, `1` = failed, `2` = error.
|
|
51
|
+
|
|
52
|
+
## Check Suites
|
|
53
|
+
|
|
54
|
+
### Security (5 checks)
|
|
55
|
+
|
|
56
|
+
| Check | Severity | What it catches |
|
|
57
|
+
|-------|----------|-----------------|
|
|
58
|
+
| Hardcoded Secrets | critical | API keys, tokens, JWTs in source code |
|
|
59
|
+
| Service Key Exposure | critical | Supabase service role keys in frontend |
|
|
60
|
+
| Deprecated Supabase Admin | high | Legacy `createClient(serviceKey)` patterns |
|
|
61
|
+
| SystemDB Whitelist | high | Unauthorized system database access |
|
|
62
|
+
| Gitignore Validation | high | Missing .gitignore entries, tracked .env files |
|
|
63
|
+
|
|
64
|
+
### Stability (3 checks)
|
|
65
|
+
|
|
66
|
+
| Check | Severity | What it catches |
|
|
67
|
+
|-------|----------|-----------------|
|
|
68
|
+
| Husky Hooks | medium | Missing pre-commit/pre-push hooks |
|
|
69
|
+
| CI Pipeline | medium | Missing or incomplete GitHub Actions config |
|
|
70
|
+
| NPM Audit | high | Known vulnerabilities in dependencies |
|
|
71
|
+
|
|
72
|
+
### Code Quality (1 check)
|
|
73
|
+
|
|
74
|
+
| Check | Severity | What it catches |
|
|
75
|
+
|-------|----------|-----------------|
|
|
76
|
+
| API Response Format | medium | Non-standard `{ success, data }` response format |
|
|
77
|
+
|
|
78
|
+
### Supabase (2 checks, auto-detected)
|
|
79
|
+
|
|
80
|
+
| Check | Severity | What it catches |
|
|
81
|
+
|-------|----------|-----------------|
|
|
82
|
+
| RLS Policy Audit | critical | Tables without Row Level Security |
|
|
83
|
+
| RPC Param Mismatch | critical | TypeScript `.rpc()` calls with wrong parameter names vs SQL |
|
|
84
|
+
|
|
85
|
+
### Hygiene (1 check)
|
|
86
|
+
|
|
87
|
+
| Check | Severity | What it catches |
|
|
88
|
+
|-------|----------|-----------------|
|
|
89
|
+
| File Organization | high | Stray .md, .sh, clutter in code dirs, root mess, nested docs/ |
|
|
90
|
+
|
|
91
|
+
## Health Checks
|
|
92
|
+
|
|
93
|
+
Separate from audit suites, health checks provide a scored assessment (0-N points) used by the Ralph Manager dashboard:
|
|
94
|
+
|
|
95
|
+
| Check | Max | What it measures |
|
|
96
|
+
|-------|-----|------------------|
|
|
97
|
+
| File Organization | 6pt | Docs in /docs, scripts in /scripts, clean root & code dirs |
|
|
98
|
+
| Git | 4pt | Clean working tree, branch hygiene, commit frequency |
|
|
99
|
+
| Gitignore | 3pt | Critical entries present |
|
|
100
|
+
| CLAUDE.md | 3pt | Project instructions for AI assistants |
|
|
101
|
+
| Secrets | 3pt | No exposed secrets |
|
|
102
|
+
| Tests | 4pt | Test framework, coverage, test files |
|
|
103
|
+
| Naming Conventions | 5pt | File/dir naming consistency |
|
|
104
|
+
| Infrastructure YML | 3pt | Railway/Docker config |
|
|
105
|
+
| Doppler Compliance | 2pt | Secrets management via Doppler |
|
|
106
|
+
| MCP Servers | 2pt | MCP configuration |
|
|
107
|
+
| Stella Integration | 2pt | Stella package integration |
|
|
108
|
+
| Quality Toolkit | 2pt | Tetra dev-toolkit installed |
|
|
109
|
+
| Repo Visibility | 1pt | Private repo |
|
|
110
|
+
| RLS Audit | 3pt | Row Level Security policies |
|
|
111
|
+
| Plugins | 2pt | Claude Code plugin config |
|
|
112
|
+
| VinciFox Widget | 1pt | Widget installation |
|
|
113
|
+
|
|
114
|
+
## Auto-fix: Cleanup Script
|
|
115
|
+
|
|
116
|
+
For hygiene issues, an auto-fix script is included:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Dry run (shows what would change)
|
|
120
|
+
bash node_modules/@soulbatical/tetra-dev-toolkit/bin/cleanup-repos.sh
|
|
121
|
+
|
|
122
|
+
# Execute
|
|
123
|
+
bash node_modules/@soulbatical/tetra-dev-toolkit/bin/cleanup-repos.sh --execute
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
What it does:
|
|
127
|
+
- `.md` files in code dirs -> `docs/_moved/`
|
|
128
|
+
- `.sh` scripts in code dirs -> `scripts/_moved/`
|
|
129
|
+
- Root clutter (`.txt`, `.png`, `.csv`) -> `docs/_cleanup/`
|
|
130
|
+
- Code dir clutter -> `docs/_cleanup/{dir}/`
|
|
131
|
+
- `.env` secrets in code dirs -> deleted
|
|
132
|
+
- `tmp/`, `logs/`, `data/` dirs -> added to `.gitignore`
|
|
133
|
+
|
|
134
|
+
## Configuration
|
|
135
|
+
|
|
136
|
+
Override defaults in `.tetra-quality.json` or `"tetra-quality"` key in `package.json`:
|
|
137
|
+
|
|
138
|
+
```json
|
|
139
|
+
{
|
|
140
|
+
"suites": {
|
|
141
|
+
"security": true,
|
|
142
|
+
"stability": true,
|
|
143
|
+
"codeQuality": true,
|
|
144
|
+
"supabase": "auto",
|
|
145
|
+
"hygiene": true
|
|
146
|
+
},
|
|
147
|
+
"supabase": {
|
|
148
|
+
"publicRpcFunctions": ["get_public_data"],
|
|
149
|
+
"publicTables": ["public_lookup"]
|
|
150
|
+
},
|
|
151
|
+
"stability": {
|
|
152
|
+
"allowedVulnerabilities": {
|
|
153
|
+
"critical": 0,
|
|
154
|
+
"high": 0,
|
|
155
|
+
"moderate": 10
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## CLI Tools
|
|
162
|
+
|
|
163
|
+
| Command | Description |
|
|
164
|
+
|---------|-------------|
|
|
165
|
+
| `tetra-audit` | Run quality/security/hygiene checks |
|
|
166
|
+
| `tetra-setup` | Install hooks, CI, and config |
|
|
167
|
+
| `tetra-dev-token` | Generate development tokens |
|
|
168
|
+
|
|
169
|
+
## Changelog
|
|
170
|
+
|
|
171
|
+
### 1.3.0 (2025-02-21)
|
|
172
|
+
|
|
173
|
+
**New: Hygiene suite**
|
|
174
|
+
- Added `tetra-audit hygiene` — detects stray docs, scripts, clutter in code dirs, root mess
|
|
175
|
+
- Added `cleanup-repos.sh` auto-fix script in `bin/`
|
|
176
|
+
- `tetra-setup hooks` now creates pre-push hook with hygiene gate
|
|
177
|
+
- Re-running `tetra-setup hooks` on existing repos adds hygiene check without overwriting
|
|
178
|
+
|
|
179
|
+
**New: RPC Param Mismatch check**
|
|
180
|
+
- Added `rpc-param-mismatch` check in supabase suite
|
|
181
|
+
- Statically compares `.rpc()` calls in TypeScript with SQL function parameter names
|
|
182
|
+
- Catches PGRST202 errors before they hit production
|
|
183
|
+
|
|
184
|
+
**Improved: File Organization health check**
|
|
185
|
+
- Extended from 5pt to 6pt — added root clutter detection
|
|
186
|
+
- Root clutter: `.txt`, `.png`, `.csv`, `.pdf`, `.py` files and `tmp/`, `logs/`, `data/` dirs
|
|
187
|
+
- Gitignored dirs no longer counted as clutter
|
|
188
|
+
- `CLAUDE.md` allowed anywhere (not just root)
|
|
189
|
+
|
|
190
|
+
### 1.2.0
|
|
191
|
+
|
|
192
|
+
- Initial public version
|
|
193
|
+
- Security suite: hardcoded secrets, service key exposure, deprecated admin, systemdb whitelist, gitignore
|
|
194
|
+
- Stability suite: husky hooks, CI pipeline, npm audit
|
|
195
|
+
- Code quality suite: API response format
|
|
196
|
+
- Supabase suite: RLS policy audit
|
|
197
|
+
- Health checks: 16 checks, max 37pt
|
|
198
|
+
- CLI: `tetra-audit`, `tetra-setup`, `tetra-dev-token`
|
|
@@ -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,
|