Forked clean from public hackathon repo (Starfire-AgentTeam, BSL 1.1) with full rebrand to Molecule AI under github.com/Molecule-AI/molecule-monorepo. Brand: Starfire → Molecule AI. Slug: starfire / agent-molecule → molecule. Env vars: STARFIRE_* → MOLECULE_*. Go module: github.com/agent-molecule/platform → github.com/Molecule-AI/molecule-monorepo/platform. Python packages: starfire_plugin → molecule_plugin, starfire_agent → molecule_agent. DB: agentmolecule → molecule. History truncated; see public repo for prior commits and contributor attribution. Verified green: go test -race ./... (platform), pytest (workspace-template 1129 + sdk 132), vitest (canvas 352), build (mcp). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
125 lines
6.1 KiB
Bash
Executable File
125 lines
6.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Pre-commit hook — enforces Starfire codebase conventions.
|
|
# Install: git config core.hooksPath .githooks
|
|
#
|
|
# Checks run ONLY on staged files to keep commits fast.
|
|
# If any check fails, the commit is rejected — the agent must fix it.
|
|
|
|
set -euo pipefail
|
|
|
|
ERRORS=0
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# 1. Canvas: 'use client' directive on hook-using components
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
STAGED_TSX=$(git diff --cached --name-only --diff-filter=ACM | grep '\.tsx$' | grep 'canvas/src/' || true)
|
|
|
|
if [ -n "$STAGED_TSX" ]; then
|
|
for f in $STAGED_TSX; do
|
|
# Skip test files
|
|
if echo "$f" | grep -q "__tests__\|\.test\."; then
|
|
continue
|
|
fi
|
|
# Check if file uses hooks/handlers
|
|
if grep -qE "useState|useEffect|useCallback|useMemo|useRef|useStore|onClick|onChange" "$f" 2>/dev/null; then
|
|
# Check if 'use client' is in the first 3 lines
|
|
if ! head -3 "$f" | grep -qE "use client" 2>/dev/null; then
|
|
echo "❌ MISSING 'use client': $f"
|
|
echo " This file uses React hooks but lacks the 'use client' directive."
|
|
echo " Add \"'use client';\" as the very first line."
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# 2. Canvas: No light theme colors in new/changed components
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
if [ -n "$STAGED_TSX" ]; then
|
|
for f in $STAGED_TSX; do
|
|
# Check staged diff (not full file) for white/light colors
|
|
DIFF=$(git diff --cached "$f" | grep '^+' | grep -v '^+++' || true)
|
|
if echo "$DIFF" | grep -qiE 'background:\s*#fff|background:\s*white|bg-white|bg-gray-[12]00' 2>/dev/null; then
|
|
echo "⚠️ LIGHT THEME COLOR in $f — use zinc-900/950 backgrounds, not white/gray"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
done
|
|
fi
|
|
|
|
STAGED_CSS=$(git diff --cached --name-only --diff-filter=ACM | grep '\.css$' | grep 'canvas/src/' || true)
|
|
if [ -n "$STAGED_CSS" ]; then
|
|
for f in $STAGED_CSS; do
|
|
DIFF=$(git diff --cached "$f" | grep '^+' | grep -v '^+++' || true)
|
|
if echo "$DIFF" | grep -qiE 'background:\s*#fff|background:\s*white' 2>/dev/null; then
|
|
echo "⚠️ LIGHT THEME COLOR in $f — use zinc-900 (#18181b), not white"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# 3. Python: No bare except pass (silent swallowing)
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
STAGED_PY=$(git diff --cached --name-only --diff-filter=ACM | grep '\.py$' | grep 'workspace-template/' || true)
|
|
|
|
if [ -n "$STAGED_PY" ]; then
|
|
for f in $STAGED_PY; do
|
|
DIFF=$(git diff --cached "$f" | grep '^+' | grep -v '^+++' || true)
|
|
if echo "$DIFF" | grep -qE '^\+\s*except.*:\s*$' 2>/dev/null; then
|
|
NEXT_LINE=$(echo "$DIFF" | grep -A1 '^\+\s*except.*:\s*$' | tail -1)
|
|
if echo "$NEXT_LINE" | grep -qE '^\+\s*pass\s*$' 2>/dev/null; then
|
|
echo "⚠️ SILENT EXCEPTION SWALLOW in $f — add logger.debug() instead of bare 'pass'"
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# 4. Go: No string-concatenated SQL
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
STAGED_GO=$(git diff --cached --name-only --diff-filter=ACM | grep '\.go$' | grep 'platform/' || true)
|
|
|
|
if [ -n "$STAGED_GO" ]; then
|
|
for f in $STAGED_GO; do
|
|
DIFF=$(git diff --cached "$f" | grep '^+' | grep -v '^+++' || true)
|
|
if echo "$DIFF" | grep -qE 'fmt\.Sprintf.*SELECT|fmt\.Sprintf.*INSERT|fmt\.Sprintf.*UPDATE|fmt\.Sprintf.*DELETE' 2>/dev/null; then
|
|
echo "❌ SQL INJECTION RISK in $f — use parameterized queries (\$1, \$2), not fmt.Sprintf"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# 5. Secrets: No tokens/keys in staged files
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
ALL_STAGED=$(git diff --cached --name-only --diff-filter=ACM || true)
|
|
if [ -n "$ALL_STAGED" ]; then
|
|
for f in $ALL_STAGED; do
|
|
# Skip binary, known safe files, hooks, docs, and markdown
|
|
if echo "$f" | grep -qE '\.png$|\.jpg$|\.ico$|\.woff|node_modules|\.lock$|\.githooks/|\.md$|docs/'; then
|
|
continue
|
|
fi
|
|
DIFF=$(git diff --cached "$f" 2>/dev/null | grep '^+' | grep -v '^+++' || true)
|
|
if echo "$DIFF" | grep -qE 'sk-ant-|sk-proj-|ghp_|gho_|AKIA[A-Z0-9]' 2>/dev/null; then
|
|
echo "❌ POSSIBLE SECRET in $f — do not commit API keys or tokens"
|
|
ERRORS=$((ERRORS + 1))
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# ──────────────────────────────────────────────────────────
|
|
# Result
|
|
# ──────────────────────────────────────────────────────────
|
|
|
|
if [ "$ERRORS" -gt 0 ]; then
|
|
echo ""
|
|
echo "🚫 Pre-commit check failed with $ERRORS error(s). Fix them and try again."
|
|
exit 1
|
|
fi
|