Files
dss/tools/discovery/discover.sh
Digital Production Factory 276ed71f31 Initial commit: Clean DSS implementation
Migrated from design-system-swarm with fresh git history.
Old project history preserved in /home/overbits/apps/design-system-swarm

Core components:
- MCP Server (Python FastAPI with mcp 1.23.1)
- Claude Plugin (agents, commands, skills, strategies, hooks, core)
- DSS Backend (dss-mvp1 - token translation, Figma sync)
- Admin UI (Node.js/React)
- Server (Node.js/Express)
- Storybook integration (dss-mvp1/.storybook)

Self-contained configuration:
- All paths relative or use DSS_BASE_PATH=/home/overbits/dss
- PYTHONPATH configured for dss-mvp1 and dss-claude-plugin
- .env file with all configuration
- Claude plugin uses ${CLAUDE_PLUGIN_ROOT} for portability

Migration completed: $(date)
🤖 Clean migration with full functionality preserved
2025-12-09 18:45:48 -03:00

331 lines
9.7 KiB
Bash
Executable File

#!/bin/bash
#
# Design System Server (DSS) - Project Discovery Script
#
# Non-intrusive analysis of project structure, dependencies, and health.
# Outputs JSON for UI consumption.
#
# Usage: ./discover.sh [project_path] [--full]
#
set -e
PROJECT_PATH="${1:-.}"
FULL_SCAN="${2:-}"
OUTPUT_DIR="${PROJECT_PATH}/.dss"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
# Create output directory
mkdir -p "$OUTPUT_DIR"
# Colors for terminal output (only if interactive)
if [ -t 1 ]; then
GREEN='\033[0;32m'
BLUE='\033[0;34m'
YELLOW='\033[1;33m'
NC='\033[0m'
else
GREEN=''
BLUE=''
YELLOW=''
NC=''
fi
log() {
echo -e "${BLUE}[DSS]${NC} $1" >&2
}
# === Project Type Detection ===
detect_project_type() {
local types=()
[ -f "$PROJECT_PATH/package.json" ] && types+=("nodejs")
[ -f "$PROJECT_PATH/requirements.txt" ] || [ -f "$PROJECT_PATH/pyproject.toml" ] && types+=("python")
[ -f "$PROJECT_PATH/Cargo.toml" ] && types+=("rust")
[ -f "$PROJECT_PATH/go.mod" ] && types+=("go")
[ -f "$PROJECT_PATH/pom.xml" ] || [ -f "$PROJECT_PATH/build.gradle" ] && types+=("java")
[ -f "$PROJECT_PATH/Gemfile" ] && types+=("ruby")
[ -f "$PROJECT_PATH/composer.json" ] && types+=("php")
echo "${types[@]:-unknown}"
}
# === Framework Detection ===
detect_frameworks() {
local frameworks=()
if [ -f "$PROJECT_PATH/package.json" ]; then
local pkg=$(cat "$PROJECT_PATH/package.json")
echo "$pkg" | grep -q '"react"' && frameworks+=("react")
echo "$pkg" | grep -q '"vue"' && frameworks+=("vue")
echo "$pkg" | grep -q '"@angular/core"' && frameworks+=("angular")
echo "$pkg" | grep -q '"svelte"' && frameworks+=("svelte")
echo "$pkg" | grep -q '"next"' && frameworks+=("nextjs")
echo "$pkg" | grep -q '"nuxt"' && frameworks+=("nuxt")
echo "$pkg" | grep -q '"express"' && frameworks+=("express")
echo "$pkg" | grep -q '"fastify"' && frameworks+=("fastify")
echo "$pkg" | grep -q '"tailwindcss"' && frameworks+=("tailwind")
echo "$pkg" | grep -q '"@emotion"' && frameworks+=("emotion")
echo "$pkg" | grep -q '"styled-components"' && frameworks+=("styled-components")
fi
if [ -f "$PROJECT_PATH/requirements.txt" ]; then
grep -q "fastapi" "$PROJECT_PATH/requirements.txt" && frameworks+=("fastapi")
grep -q "django" "$PROJECT_PATH/requirements.txt" && frameworks+=("django")
grep -q "flask" "$PROJECT_PATH/requirements.txt" && frameworks+=("flask")
fi
echo "${frameworks[@]:-none}"
}
# === Design System Detection ===
detect_design_system() {
local ds_info='{"detected":false}'
# Check for common design system indicators
if [ -f "$PROJECT_PATH/package.json" ]; then
local pkg=$(cat "$PROJECT_PATH/package.json")
if echo "$pkg" | grep -qE '"(@chakra-ui|@mui|antd|@radix-ui|@headlessui)"'; then
ds_info='{"detected":true,"type":"library"}'
fi
fi
# Check for custom design tokens
if find "$PROJECT_PATH" -maxdepth 3 -name "tokens.css" -o -name "tokens.json" -o -name "design-tokens.*" 2>/dev/null | grep -q .; then
ds_info='{"detected":true,"type":"custom","has_tokens":true}'
fi
# Check for Figma integration
if find "$PROJECT_PATH" -maxdepth 3 -name ".figmarc" -o -name "figma.config.*" 2>/dev/null | grep -q .; then
ds_info=$(echo "$ds_info" | sed 's/}$/,"figma_connected":true}/')
fi
echo "$ds_info"
}
# === File Statistics ===
get_file_stats() {
local total_files=$(find "$PROJECT_PATH" -type f ! -path "*/node_modules/*" ! -path "*/.git/*" ! -path "*/dist/*" ! -path "*/__pycache__/*" 2>/dev/null | wc -l)
local js_files=$(find "$PROJECT_PATH" -type f \( -name "*.js" -o -name "*.jsx" -o -name "*.ts" -o -name "*.tsx" \) ! -path "*/node_modules/*" 2>/dev/null | wc -l)
local css_files=$(find "$PROJECT_PATH" -type f \( -name "*.css" -o -name "*.scss" -o -name "*.less" \) ! -path "*/node_modules/*" 2>/dev/null | wc -l)
local py_files=$(find "$PROJECT_PATH" -type f -name "*.py" ! -path "*/__pycache__/*" 2>/dev/null | wc -l)
local component_files=$(find "$PROJECT_PATH" -type f \( -name "*.jsx" -o -name "*.tsx" -o -name "*.vue" -o -name "*.svelte" \) ! -path "*/node_modules/*" 2>/dev/null | wc -l)
cat <<EOF
{
"total": $total_files,
"javascript": $js_files,
"css": $css_files,
"python": $py_files,
"components": $component_files
}
EOF
}
# === Dependency Analysis ===
analyze_dependencies() {
local deps='{"production":[],"development":[],"total":0}'
if [ -f "$PROJECT_PATH/package.json" ]; then
local prod_count=$(jq '.dependencies | length // 0' "$PROJECT_PATH/package.json" 2>/dev/null || echo 0)
local dev_count=$(jq '.devDependencies | length // 0' "$PROJECT_PATH/package.json" 2>/dev/null || echo 0)
local total=$((prod_count + dev_count))
deps="{\"production\":$prod_count,\"development\":$dev_count,\"total\":$total}"
fi
if [ -f "$PROJECT_PATH/requirements.txt" ]; then
local py_deps=$(grep -v "^#" "$PROJECT_PATH/requirements.txt" | grep -v "^$" | wc -l)
deps="{\"python\":$py_deps,\"total\":$py_deps}"
fi
echo "$deps"
}
# === Git Analysis ===
analyze_git() {
if [ ! -d "$PROJECT_PATH/.git" ]; then
echo '{"is_repo":false}'
return
fi
cd "$PROJECT_PATH"
local branch=$(git branch --show-current 2>/dev/null || echo "unknown")
local commits=$(git rev-list --count HEAD 2>/dev/null || echo 0)
local contributors=$(git log --format='%ae' | sort -u | wc -l 2>/dev/null || echo 0)
local last_commit=$(git log -1 --format='%ci' 2>/dev/null || echo "unknown")
local uncommitted=$(git status --porcelain 2>/dev/null | wc -l || echo 0)
cat <<EOF
{
"is_repo": true,
"branch": "$branch",
"commits": $commits,
"contributors": $contributors,
"last_commit": "$last_commit",
"uncommitted_changes": $uncommitted
}
EOF
}
# === Component Discovery ===
discover_components() {
local components=()
# Find component files
while IFS= read -r file; do
if [ -n "$file" ]; then
local name=$(basename "$file" | sed 's/\.[^.]*$//')
local dir=$(dirname "$file" | sed "s|^$PROJECT_PATH/||")
components+=("{\"name\":\"$name\",\"path\":\"$dir\",\"file\":\"$(basename "$file")\"}")
fi
done < <(find "$PROJECT_PATH" -type f \( -name "*.jsx" -o -name "*.tsx" -o -name "*.vue" \) ! -path "*/node_modules/*" ! -path "*/.next/*" ! -path "*/dist/*" 2>/dev/null | head -50)
# Join array
local joined=$(IFS=,; echo "${components[*]}")
echo "[$joined]"
}
# === Health Score ===
calculate_health_score() {
local score=100
local issues=()
# Check for package-lock or yarn.lock
if [ -f "$PROJECT_PATH/package.json" ]; then
if [ ! -f "$PROJECT_PATH/package-lock.json" ] && [ ! -f "$PROJECT_PATH/yarn.lock" ] && [ ! -f "$PROJECT_PATH/pnpm-lock.yaml" ]; then
score=$((score - 10))
issues+=("\"No lock file found\"")
fi
fi
# Check for .gitignore
if [ -d "$PROJECT_PATH/.git" ] && [ ! -f "$PROJECT_PATH/.gitignore" ]; then
score=$((score - 5))
issues+=("\"Missing .gitignore\"")
fi
# Check for README
if [ ! -f "$PROJECT_PATH/README.md" ] && [ ! -f "$PROJECT_PATH/README" ]; then
score=$((score - 5))
issues+=("\"Missing README\"")
fi
# Check for tests
if ! find "$PROJECT_PATH" -maxdepth 3 -type d \( -name "test" -o -name "tests" -o -name "__tests__" -o -name "spec" \) 2>/dev/null | grep -q .; then
score=$((score - 10))
issues+=("\"No test directory found\"")
fi
# Check for TypeScript
if [ -f "$PROJECT_PATH/package.json" ] && ! [ -f "$PROJECT_PATH/tsconfig.json" ]; then
if grep -q "typescript" "$PROJECT_PATH/package.json" 2>/dev/null; then
score=$((score - 5))
issues+=("\"TypeScript installed but no tsconfig.json\"")
fi
fi
local joined_issues=$(IFS=,; echo "${issues[*]}")
cat <<EOF
{
"score": $score,
"grade": "$([ $score -ge 90 ] && echo 'A' || ([ $score -ge 80 ] && echo 'B' || ([ $score -ge 70 ] && echo 'C' || ([ $score -ge 60 ] && echo 'D' || echo 'F'))))",
"issues": [$joined_issues]
}
EOF
}
# === CSS Analysis ===
analyze_css() {
local css_files=$(find "$PROJECT_PATH" -type f \( -name "*.css" -o -name "*.scss" \) ! -path "*/node_modules/*" 2>/dev/null)
local total_files=$(echo "$css_files" | grep -c . || echo 0)
local has_variables=false
local has_custom_properties=false
local preprocessor="none"
if echo "$css_files" | grep -q ".scss"; then
preprocessor="sass"
fi
if [ -n "$css_files" ]; then
for file in $css_files; do
if grep -q -- "--" "$file" 2>/dev/null; then
has_custom_properties=true
fi
if grep -q "\\\$" "$file" 2>/dev/null; then
has_variables=true
fi
done
fi
cat <<EOF
{
"files": $total_files,
"preprocessor": "$preprocessor",
"has_css_variables": $has_custom_properties,
"has_preprocessor_variables": $has_variables
}
EOF
}
# === Main Discovery ===
log "Starting project discovery..."
PROJECT_TYPES=$(detect_project_type)
FRAMEWORKS=$(detect_frameworks)
DESIGN_SYSTEM=$(detect_design_system)
FILE_STATS=$(get_file_stats)
DEPENDENCIES=$(analyze_dependencies)
GIT_INFO=$(analyze_git)
HEALTH=$(calculate_health_score)
CSS_INFO=$(analyze_css)
if [ "$FULL_SCAN" = "--full" ]; then
COMPONENTS=$(discover_components)
else
COMPONENTS="[]"
fi
# Build final JSON
cat > "$OUTPUT_DIR/discovery.json" <<EOF
{
"meta": {
"version": "1.0.0",
"timestamp": "$TIMESTAMP",
"project_path": "$PROJECT_PATH",
"full_scan": $([ "$FULL_SCAN" = "--full" ] && echo true || echo false)
},
"project": {
"types": $(echo "$PROJECT_TYPES" | jq -R 'split(" ")' 2>/dev/null || echo '["unknown"]'),
"frameworks": $(echo "$FRAMEWORKS" | jq -R 'split(" ")' 2>/dev/null || echo '[]')
},
"design_system": $DESIGN_SYSTEM,
"files": $FILE_STATS,
"dependencies": $DEPENDENCIES,
"git": $GIT_INFO,
"health": $HEALTH,
"css": $CSS_INFO,
"components": $COMPONENTS
}
EOF
log "Discovery complete: $OUTPUT_DIR/discovery.json"
# Output the JSON
cat "$OUTPUT_DIR/discovery.json"